-
-
Notifications
You must be signed in to change notification settings - Fork 299
Cloud Runner Improvements - S3 Locking, Aws Local Stack (Pipelines), Testing Improvements, Rclone storage support, Provider plugin system #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Implemented a primary attempt to pull LFS files using GIT_PRIVATE_TOKEN. - Added a fallback mechanism to use GITHUB_TOKEN if the initial attempt fails. - Configured git to replace SSH and HTTPS URLs with token-based authentication for the fallback. - Improved error handling to log specific failure messages for both token attempts. This change ensures more robust handling of LFS file retrieval in various authentication scenarios.
- Added permissions for packages, pull-requests, statuses, and id-token to enhance workflow capabilities. - This change improves the CI pipeline's ability to manage pull requests and access necessary resources.
…ation - Added configuration to use GIT_PRIVATE_TOKEN for git operations, replacing SSH and HTTPS URLs with token-based authentication. - Improved error handling to ensure GIT_PRIVATE_TOKEN availability before attempting to pull LFS files. - This change streamlines the process of pulling LFS files in environments requiring token authentication.
…entication - Enhanced the process of configuring git to use GIT_PRIVATE_TOKEN and GITHUB_TOKEN by clearing existing URL configurations before setting new ones. - Improved the clarity of the URL replacement commands for better readability and maintainability. - This change ensures a more robust setup for pulling LFS files in environments requiring token authentication.
… pipeline - Replaced instances of GITHUB_TOKEN with GIT_PRIVATE_TOKEN in the cloud-runner CI pipeline configuration. - This change ensures consistent use of token-based authentication across various jobs in the workflow, enhancing security and functionality.
…L unsetting - Modified the git configuration commands to append '|| true' to prevent errors if the specified URLs do not exist. - This change enhances the reliability of the URL clearing process in the RemoteClient class, ensuring smoother execution during token-based authentication setups.
…tion - Updated comments for clarity regarding the purpose of URL configuration changes. - Simplified the git configuration commands by removing redundant lines while maintaining functionality for HTTPS token-based authentication. - This change enhances the readability and maintainability of the RemoteClient class's git setup process.
# Conflicts: # dist/index.js # dist/index.js.map # jest.config.js # yarn.lock
… log lines for test assertions
…off; lint/format fixes
… cache key for retained workspace (#379)
…logs; tests: retained workspace AWS assertion (#381)
…rd local provider steps
…nd log management; update builder path logic based on provider strategy
…sed on provider strategy and credentials; update binary files
…ained markers; hooks: include AWS S3 hooks on aws provider
…t:ci script; fix(windows): skip grep-based version regex tests; logs: echo CACHE_KEY/retained markers; hooks: include AWS hooks on aws provider
… update binary files
…rintf for empty input
…I hangs; s3 steps pass again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts (2)
13-19: Events fetch still lacks error handling and can mask pod failures.This is a duplicate of a previous review comment. If
listNamespacedEventfails due to RBAC restrictions, API timeouts, or other errors, the exception will propagate and obscure the actual pod failure. Since events are diagnostic-only, a failure here shouldn't prevent reporting the pod's failed state.Apply the suggested fix from the previous review:
- const events = (await kubeClient.listNamespacedEvent(namespace)).body.items - .filter((x) => x.involvedObject?.name === podName) - .map((x) => ({ - message: x.message || '', - reason: x.reason || '', - type: x.type || '', - })); + let events: Array<{ message: string; reason: string; type: string }> = []; + try { + events = (await kubeClient.listNamespacedEvent(namespace)).body.items + .filter((x) => x.involvedObject?.name === podName) + .map((x) => ({ + message: x.message || '', + reason: x.reason || '', + type: x.type || '', + })); + } catch (eventError) { + CloudRunnerLogger.logWarning(`Failed to fetch pod events: ${eventError}`); + }
35-58: Multi-container logic flaw persists: only the last container's exit status is retained.This is a duplicate of a previous review comment. The loop iterates over all container statuses but overwrites
containerExitCodeandcontainerSucceededon each iteration. If a pod has multiple containers and the last one succeeded but an earlier one failed, the code will incorrectly treat the pod as successful and skip error throwing at line 98. This critical flaw also affects the PreStopHook handling logic at lines 64-84, which relies on accurate container exit status.Apply the suggested fix from the previous review to check all containers:
- let containerExitCode: number | undefined; - let containerSucceeded = false; + let allContainersSucceeded = true; + let anyContainerExitCode: number | undefined; if (containerStatuses.length > 0) { containerStatuses.forEach((cs, idx) => { if (cs.state?.waiting) { errorDetails.push( `Container ${idx} (${cs.name}) waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message || ''}`, ); } if (cs.state?.terminated) { const exitCode = cs.state.terminated.exitCode; - containerExitCode = exitCode; + anyContainerExitCode = exitCode; if (exitCode === 0) { - containerSucceeded = true; + // Keep checking all containers + } else { + allContainersSucceeded = false; } errorDetails.push( `Container ${idx} (${cs.name}) terminated: ${cs.state.terminated.reason} - ${ cs.state.terminated.message || '' } (exit code: ${exitCode})`, ); } }); }Then update lines 70 and 88 to use
allContainersSucceededandanyContainerExitCode.
🧹 Nitpick comments (2)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts (1)
86-94: Clarify comment about error throwing behavior.The comment at lines 92-93 states "Still throw error for now" but there's no explicit throw statement in this block. The code falls through to line 98 where the error is actually thrown. Consider updating the comment to be more explicit about the fall-through behavior.
- // Still throw error for now, but with more context - // The task runner will retry and get the actual container status + // Fall through to throw error with enhanced context + // The task runner will retry and should get the actual container statussrc/model/cloud-runner/workflows/custom-workflow.ts (1)
46-62: UseCloudRunnerLogger.errorfor build-stopping failures.At lines 58-60, when
allowFailureis false and the build stops, useerrorinstead oflogfor proper severity signaling in CI logs. This matches the warning level used when the step fails but continues.if (allowFailure) { CloudRunnerLogger.logWarning( `Hook container "${stepName}" failed but allowFailure is true. Continuing build. Error: ${ error?.message || error }`, ); // Continue to next step } else { - CloudRunnerLogger.log( + CloudRunnerLogger.error( `Hook container "${stepName}" failed and allowFailure is false (default). Stopping build.`, ); throw error; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts(1 hunks)src/model/cloud-runner/workflows/custom-workflow.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-30T18:01:50.339Z
Learnt from: MichaelBuhler
Repo: game-ci/unity-builder PR: 685
File: .github/workflows/build-tests-ubuntu.yml:146-146
Timestamp: 2025-01-30T18:01:50.339Z
Learning: The build steps duplication in the workflow is intentional to implement a retry mechanism with different delay intervals between attempts.
Applied to files:
src/model/cloud-runner/workflows/custom-workflow.ts
🧬 Code graph analysis (1)
src/model/cloud-runner/workflows/custom-workflow.ts (2)
src/model/cloud-runner/options/cloud-runner-folders.ts (1)
CloudRunnerFolders(6-90)src/model/cloud-runner/services/core/cloud-runner-logger.ts (1)
error(24-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: Tests
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: WebGL on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: iOS on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
src/model/cloud-runner/remote-client/caching.ts (3)
121-126: Command injection concern remains unaddressed.The shell command at line 123 interpolates
cacheArtifactNameandcompressionSuffixdirectly. This was previously flagged - the sanitization at line 48 (removing spaces) is insufficient to prevent command injection via shell metacharacters.
132-136: String-based error detection remains fragile.The error detection at line 135 checking for
'No space left'or'Wrote only'was previously flagged. Different systems, tar implementations, or locales may produce different error messages. Consider also checking forENOSPCor the error code:const isSpaceError = errorMessage.includes('No space left') || errorMessage.includes('Wrote only') || errorMessage.includes('ENOSPC') || error?.code === 'ENOSPC';
101-111: Cleanup may target the working directory whencacheFolderis a relative path.When
cacheFolderis'cache'(the default perCloudRunnerFolders.cacheFolder),path.dirname(cacheFolder)returns'.'. This causes thefindcommands at lines 106 and 110 to scan the current working directory, potentially deleting unrelated*.tar*files or directories.try { const cacheParent = path.dirname(cacheFolder); - if (await fileExists(cacheParent)) { + // Only clean up if cacheFolder is an absolute path with a distinct parent + if (cacheParent !== '.' && cacheParent !== cacheFolder && await fileExists(cacheParent)) {Alternatively, restrict cleanup to the cache folder itself rather than its parent.
🧹 Nitpick comments (1)
src/model/cloud-runner/remote-client/caching.ts (1)
85-91: Fragile disk usage parsing.The regex
/(\d+)%/captures the first percentage indfoutput, butdfoutput format varies across systems and locales. Some systems show capacity percentage in different columns, and the mount point might also contain numbers followed by%in edge cases.Consider parsing specific columns or using
df --output=pcentwhere available:- const diskCheckOutput = await CloudRunnerSystem.Run(`df . 2>/dev/null || df /data 2>/dev/null || true`); + const diskCheckOutput = await CloudRunnerSystem.Run(`df --output=pcent . 2>/dev/null | tail -1 || df . 2>/dev/null | awk 'NR==2 {print $5}' || true`); CloudRunnerLogger.log(`Disk space before tar: ${diskCheckOutput}`); - // Parse disk usage percentage (e.g., "72G 72G 196M 100%") - const usageMatch = diskCheckOutput.match(/(\d+)%/); + const usageMatch = diskCheckOutput.trim().match(/(\d+)%?$/);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (1)
src/model/cloud-runner/remote-client/caching.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/model/cloud-runner/remote-client/caching.ts (2)
src/model/cloud-runner/services/core/cloud-runner-system.ts (1)
CloudRunnerSystem(4-69)src/model/cloud-runner/options/cloud-runner-folders.ts (1)
cacheFolder(87-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: WebGL on 6000.0.36f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: WebGL on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts (2)
13-19: Wrap events fetch in try-catch to prevent masking pod failures.If
listNamespacedEventfails (e.g., RBAC restrictions, API timeout), the exception will propagate and obscure the actual pod failure. Since events are diagnostic-only, a failure here shouldn't prevent reporting the pod's failed state.This issue was previously identified in past review comments. Consider implementing the suggested fix:
- const events = (await kubeClient.listNamespacedEvent(namespace)).body.items - .filter((x) => x.involvedObject?.name === podName) - .map((x) => ({ - message: x.message || '', - reason: x.reason || '', - type: x.type || '', - })); + let events: Array<{ message: string; reason: string; type: string }> = []; + try { + events = (await kubeClient.listNamespacedEvent(namespace)).body.items + .filter((x) => x.involvedObject?.name === podName) + .map((x) => ({ + message: x.message || '', + reason: x.reason || '', + type: x.type || '', + })); + } catch (eventError) { + CloudRunnerLogger.logWarning(`Failed to fetch pod events: ${eventError}`); + }
35-84: Multi-container logic flaw: only the last container's exit status is retained.The loop at lines 39-57 overwrites
containerExitCodeandcontainerSucceededon each iteration. Consequently, the check at line 70 (containerSucceeded && containerExitCode === 0) only verifies whether the last container succeeded, ignoring failures in earlier containers.Scenario: If a pod has multiple containers where the last one exits with code 0 but an earlier one failed, the code will incorrectly return
falseat line 83, treating the pod as successful and bypassing the error throw at line 130.This issue was previously identified in past review comments. Consider the suggested fix to check all containers:
- let containerExitCode: number | undefined; - let containerSucceeded = false; + let allContainersSucceeded = true; + let anyContainerExitCode: number | undefined; if (containerStatuses.length > 0) { containerStatuses.forEach((cs, idx) => { if (cs.state?.waiting) { errorDetails.push( `Container ${idx} (${cs.name}) waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message || ''}`, ); } if (cs.state?.terminated) { const exitCode = cs.state.terminated.exitCode; - containerExitCode = exitCode; + anyContainerExitCode = exitCode; if (exitCode === 0) { - containerSucceeded = true; + // Keep checking all containers + } else { + allContainersSucceeded = false; } errorDetails.push( `Container ${idx} (${cs.name}) terminated: ${cs.state.terminated.reason} - ${ cs.state.terminated.message || '' } (exit code: ${exitCode})`, ); } }); }Then update line 70 to:
if (allContainersSucceeded && anyContainerExitCode === 0)src/model/cloud-runner/remote-client/caching.ts (4)
153-171:retrySucceededflag is unreachable noise; simplify the retry block.If the retry
tarcommand throws, the innercatchalready throws a new error, so execution never reaches theif (!retrySucceeded)check. If it succeeds,retrySucceededistrue, so the condition is false. The flag and subsequentifare therefore dead code.You can simplify to just a try/catch:
- // Retry the tar operation once after cleanup - let retrySucceeded = false; try { await CloudRunnerSystem.Run( `tar -cf ${cacheArtifactName}.tar${compressionSuffix} "${path.basename(sourceFolder)}"`, ); - // If retry succeeds, mark it - we'll continue normally without throwing - retrySucceeded = true; } catch (retryError: any) { throw new Error( `Failed to create cache archive after cleanup. Original error: ${errorMessage}. Retry error: ${ retryError?.message || retryError }`, ); } - // If retry succeeded, don't throw the original error - let execution continue after catch block - if (!retrySucceeded) { - throw error; - } - // If we get here, retry succeeded - execution will continue after the catch block
96-107: Restrict cache cleanupfindscope to avoid deleting unrelated.tarfiles.Using
cacheParent = path.dirname(cacheFolder)(and latercacheRoot = path.resolve(cacheParent, '..')) means the cleanup can target.or even/, and the pattern-name "*.tar*"will match any.tar-like file under those trees. On shared runners this risks deleting non-cache archives or other.tardata.Consider tightening this by:
- Limiting cleanup to a well-defined cache root (e.g.,
cacheFolderor a dedicated cache base), not its parent or grandparent.- Using a stricter filename pattern (e.g., known cache prefixes / suffixes) instead of
"*.tar*"so only your cache archives are affected.- Optionally making the TTLs (360/60/30 minutes) configurable rather than hard-coded.
Also applies to: 135-150
104-107: Shell commands interpolate unvalidated paths/artifact names, leaving command‑injection risk.
cacheArtifactName,cacheFolder,cacheParent, andcacheRootflow from CLI/env and are interpolated directly into shell commands (rm,tar,find,mv) without robust validation. The.replace(' ', '')oncacheArtifactNamedoes not prevent shell metacharacters like;,&,|,$, or backticks. A crafted artifact name or cache path could break out of the intended command and execute arbitrary shell.To harden this, either:
- Prefer Node APIs (
fs.promises.rm,fs.promises.readdir/stat, a tar library, etc.) withpath.join/path.resolveinstead of shellrm/find/tar/mv, or- Introduce strict validation/quoting for any value interpolated into shell strings (e.g., whitelisting
[A-Za-z0-9._/-]and rejecting anything else, or wrapping via a dedicated quoting helper).For example, a minimal mitigation pattern:
+const safeShellArg = (value: string): string => { + if (!/^[A-Za-z0-9._\/-]+$/.test(value)) { + throw new Error(`Invalid shell argument: ${value}`); + } + return value; +}; ... - await CloudRunnerSystem.Run(`rm -f ${cacheArtifactName}.tar${compressionSuffix} 2>/dev/null || true`); + await CloudRunnerSystem.Run( + `rm -f ${safeShellArg(cacheArtifactName)}.tar${compressionSuffix} 2>/dev/null || true`, + ); ... - `find ${cacheParent} -name "*.tar*" -type f -mmin +360 -delete 2>/dev/null || true`, + `find ${safeShellArg(cacheParent)} -name "*.tar*" -type f -mmin +360 -delete 2>/dev/null || true`,Similar treatment should be applied to the other
find,tar, andmvinvocations that include user-controlled values (including existing ones outside this diff).Also applies to: 117-120, 125-127, 135-150, 155-157
129-132: Disk‑space classification based only on error message strings is brittle.The check:
if (errorMessage.includes('No space left') || errorMessage.includes('Wrote only')) {depends on specific
tar/shell message text, which may vary by platform, locale, or implementation. This risks missing genuine ENOSPC conditions or misclassifying other errors.Prefer also keying off structured error data where available (e.g., exit code or
error.code === 'ENOSPC', or an explicit flag fromCloudRunnerSystem.Run), and include other stable tokens like'ENOSPC'in the message check.What error properties and exit-code semantics does Node.js `child_process` expose for ENOSPC conditions from a failed `tar` command, and how stable are these across platforms?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (2)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts(1 hunks)src/model/cloud-runner/remote-client/caching.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: WebGL on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: iOS on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: iOS on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (K8s)
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (LocalStack)
| if (wasKilled && hasPreStopHookFailure && containerExitCode === undefined) { | ||
| CloudRunnerLogger.log( | ||
| `Pod ${podName} was killed with PreStopHook failure, but container status not yet available. Waiting for container status...`, | ||
| ); | ||
| // Wait a bit for container status to become available (up to 30 seconds) | ||
| for (let i = 0; i < 6; i++) { | ||
| await new Promise((resolve) => setTimeout(resolve, 5000)); | ||
| try { | ||
| const updatedPod = ( | ||
| await kubeClient.listNamespacedPod(namespace) | ||
| ).body.items.find((x) => podName === x.metadata?.name); | ||
| if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) { | ||
| const updatedContainerStatus = updatedPod.status.containerStatuses[0]; | ||
| if (updatedContainerStatus.state?.terminated) { | ||
| const updatedExitCode = updatedContainerStatus.state.terminated.exitCode; | ||
| if (updatedExitCode === 0) { | ||
| CloudRunnerLogger.logWarning( | ||
| `Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`, | ||
| ); | ||
| return false; // Pod is not running, but container succeeded | ||
| } else { | ||
| CloudRunnerLogger.log( | ||
| `Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`, | ||
| ); | ||
| errorDetails.push( | ||
| `Container terminated after wait: exit code ${updatedExitCode}`, | ||
| ); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } catch (waitError) { | ||
| CloudRunnerLogger.log(`Error while waiting for container status: ${waitError}`); | ||
| } | ||
| } | ||
| CloudRunnerLogger.log( | ||
| `Container status still not available after waiting. Assuming failure due to PreStopHook issues.`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retry logic only checks the first container, inconsistent with multi-container handling.
Line 100 explicitly accesses updatedPod.status.containerStatuses[0], checking only the first container. This is inconsistent with the earlier loop (lines 39-57) that iterates over all containers, and it assumes single-container pods.
Issue: In multi-container pods, if the first container succeeds (exit code 0) but another container fails, the retry logic at line 107 will return false, incorrectly treating the pod as successful.
Consider iterating over all container statuses in the retry logic, similar to lines 39-57:
const updatedPod = (
await kubeClient.listNamespacedPod(namespace)
).body.items.find((x) => podName === x.metadata?.name);
if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) {
- const updatedContainerStatus = updatedPod.status.containerStatuses[0];
- if (updatedContainerStatus.state?.terminated) {
- const updatedExitCode = updatedContainerStatus.state.terminated.exitCode;
- if (updatedExitCode === 0) {
- CloudRunnerLogger.logWarning(
- `Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`,
- );
- return false; // Pod is not running, but container succeeded
- } else {
- CloudRunnerLogger.log(
- `Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`,
- );
- errorDetails.push(
- `Container terminated after wait: exit code ${updatedExitCode}`,
- );
- break;
+ let allSucceeded = true;
+ let anyTerminated = false;
+ for (const cs of updatedPod.status.containerStatuses) {
+ if (cs.state?.terminated) {
+ anyTerminated = true;
+ const exitCode = cs.state.terminated.exitCode;
+ if (exitCode !== 0) {
+ allSucceeded = false;
+ errorDetails.push(
+ `Container (${cs.name}) terminated after wait: exit code ${exitCode}`,
+ );
+ }
}
}
+ if (anyTerminated) {
+ if (allSucceeded) {
+ CloudRunnerLogger.logWarning(
+ `Pod ${podName} all containers succeeded after waiting. PreStopHook failure is non-fatal.`,
+ );
+ return false;
+ }
+ break; // At least one container failed
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (wasKilled && hasPreStopHookFailure && containerExitCode === undefined) { | |
| CloudRunnerLogger.log( | |
| `Pod ${podName} was killed with PreStopHook failure, but container status not yet available. Waiting for container status...`, | |
| ); | |
| // Wait a bit for container status to become available (up to 30 seconds) | |
| for (let i = 0; i < 6; i++) { | |
| await new Promise((resolve) => setTimeout(resolve, 5000)); | |
| try { | |
| const updatedPod = ( | |
| await kubeClient.listNamespacedPod(namespace) | |
| ).body.items.find((x) => podName === x.metadata?.name); | |
| if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) { | |
| const updatedContainerStatus = updatedPod.status.containerStatuses[0]; | |
| if (updatedContainerStatus.state?.terminated) { | |
| const updatedExitCode = updatedContainerStatus.state.terminated.exitCode; | |
| if (updatedExitCode === 0) { | |
| CloudRunnerLogger.logWarning( | |
| `Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`, | |
| ); | |
| return false; // Pod is not running, but container succeeded | |
| } else { | |
| CloudRunnerLogger.log( | |
| `Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`, | |
| ); | |
| errorDetails.push( | |
| `Container terminated after wait: exit code ${updatedExitCode}`, | |
| ); | |
| break; | |
| } | |
| } | |
| } | |
| } catch (waitError) { | |
| CloudRunnerLogger.log(`Error while waiting for container status: ${waitError}`); | |
| } | |
| } | |
| CloudRunnerLogger.log( | |
| `Container status still not available after waiting. Assuming failure due to PreStopHook issues.`, | |
| ); | |
| } | |
| if (wasKilled && hasPreStopHookFailure && containerExitCode === undefined) { | |
| CloudRunnerLogger.log( | |
| `Pod ${podName} was killed with PreStopHook failure, but container status not yet available. Waiting for container status...`, | |
| ); | |
| // Wait a bit for container status to become available (up to 30 seconds) | |
| for (let i = 0; i < 6; i++) { | |
| await new Promise((resolve) => setTimeout(resolve, 5000)); | |
| try { | |
| const updatedPod = ( | |
| await kubeClient.listNamespacedPod(namespace) | |
| ).body.items.find((x) => podName === x.metadata?.name); | |
| if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) { | |
| let allSucceeded = true; | |
| let anyTerminated = false; | |
| for (const cs of updatedPod.status.containerStatuses) { | |
| if (cs.state?.terminated) { | |
| anyTerminated = true; | |
| const exitCode = cs.state.terminated.exitCode; | |
| if (exitCode !== 0) { | |
| allSucceeded = false; | |
| errorDetails.push( | |
| `Container (${cs.name}) terminated after wait: exit code ${exitCode}`, | |
| ); | |
| } | |
| } | |
| } | |
| if (anyTerminated) { | |
| if (allSucceeded) { | |
| CloudRunnerLogger.logWarning( | |
| `Pod ${podName} all containers succeeded after waiting. PreStopHook failure is non-fatal.`, | |
| ); | |
| return false; | |
| } | |
| break; // At least one container failed | |
| } | |
| } | |
| } catch (waitError) { | |
| CloudRunnerLogger.log(`Error while waiting for container status: ${waitError}`); | |
| } | |
| } | |
| CloudRunnerLogger.log( | |
| `Container status still not available after waiting. Assuming failure due to PreStopHook issues.`, | |
| ); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (11)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts (3)
13-19: Wrap events fetch in try-catch to prevent masking pod failures.If
listNamespacedEventfails (e.g., RBAC restrictions, API timeout), the exception will propagate and obscure the actual pod failure. Since events are diagnostic-only, a failure here shouldn't prevent reporting the pod's failed state.Apply this diff to wrap the events fetch:
- const events = (await kubeClient.listNamespacedEvent(namespace)).body.items - .filter((x) => x.involvedObject?.name === podName) - .map((x) => ({ - message: x.message || '', - reason: x.reason || '', - type: x.type || '', - })); + let events: Array<{ message: string; reason: string; type: string }> = []; + try { + events = (await kubeClient.listNamespacedEvent(namespace)).body.items + .filter((x) => x.involvedObject?.name === podName) + .map((x) => ({ + message: x.message || '', + reason: x.reason || '', + type: x.type || '', + })); + } catch (eventError) { + CloudRunnerLogger.logWarning(`Failed to fetch pod events: ${eventError}`); + }
35-58: Multi-container logic flaw: only the last container's exit status is retained.The loop iterates over all container statuses but overwrites
containerExitCodeandcontainerSucceededon each iteration. If a pod has multiple containers and the last one succeeded but an earlier one failed, the code will incorrectly treat the pod as successful and skip error throwing at line 147.Consider checking if all containers succeeded:
- let containerExitCode: number | undefined; - let containerSucceeded = false; + let allContainersSucceeded = true; + let anyContainerExitCode: number | undefined; + let anyContainerFailed = false; if (containerStatuses.length > 0) { containerStatuses.forEach((cs, idx) => { if (cs.state?.waiting) { errorDetails.push( `Container ${idx} (${cs.name}) waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message || ''}`, ); } if (cs.state?.terminated) { const exitCode = cs.state.terminated.exitCode; - containerExitCode = exitCode; + anyContainerExitCode = exitCode; if (exitCode === 0) { - containerSucceeded = true; + // Keep checking all containers + } else { + allContainersSucceeded = false; + anyContainerFailed = true; } errorDetails.push( `Container ${idx} (${cs.name}) terminated: ${cs.state.terminated.reason} - ${ cs.state.terminated.message || '' } (exit code: ${exitCode})`, ); } }); }Then update line 71 to use
allContainersSucceeded && !anyContainerFailedand refactor the subsequent logic accordingly.
89-123: Retry logic only checks the first container, inconsistent with multi-container handling.Line 101 explicitly accesses
updatedPod.status.containerStatuses[0], checking only the first container. This is inconsistent with the earlier loop (lines 39-57) that iterates over all containers, and it assumes single-container pods.Issue: In multi-container pods, if the first container succeeds (exit code 0) but another container fails, the retry logic at line 108 will return
false, incorrectly treating the pod as successful.Consider iterating over all container statuses in the retry logic:
const updatedPod = ( await kubeClient.listNamespacedPod(namespace) ).body.items.find((x) => podName === x.metadata?.name); if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) { - const updatedContainerStatus = updatedPod.status.containerStatuses[0]; - if (updatedContainerStatus.state?.terminated) { - const updatedExitCode = updatedContainerStatus.state.terminated.exitCode; - if (updatedExitCode === 0) { - CloudRunnerLogger.logWarning( - `Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`, - ); - return false; // Pod is not running, but container succeeded - } else { - CloudRunnerLogger.log( - `Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`, - ); - errorDetails.push(`Container terminated after wait: exit code ${updatedExitCode}`); - containerExitCode = updatedExitCode; - containerSucceeded = false; - break; + let allSucceeded = true; + let anyTerminated = false; + for (const cs of updatedPod.status.containerStatuses) { + if (cs.state?.terminated) { + anyTerminated = true; + const exitCode = cs.state.terminated.exitCode; + if (exitCode !== 0) { + allSucceeded = false; + errorDetails.push( + `Container (${cs.name}) terminated after wait: exit code ${exitCode}`, + ); + } } } + if (anyTerminated) { + if (allSucceeded) { + CloudRunnerLogger.logWarning( + `Pod ${podName} all containers succeeded after waiting. PreStopHook failure is non-fatal.`, + ); + return false; + } + break; // At least one container failed + } }.github/workflows/cloud-runner-integrity.yml (7)
274-292: Replace GITHUB_TOKEN override with default job token (LocalStack job: all 10 test steps).All test steps in the localstack job override
GITHUB_TOKENwith${{ secrets.GIT_PRIVATE_TOKEN }}, violating least-privilege. KeepGIT_PRIVATE_TOKENas a separate variable; let the code's fallback logic use it. Replace across all steps at lines 291–292, 315–316, 339–340, 363–364, 387–388, 411–412, 435–436, 459–460, 483–484, and 507–508.Example diff for the first test step:
- name: Run cloud-runner-end2end-locking test timeout-minutes: 60 run: yarn run test "cloud-runner-end2end-locking" --detectOpenHandles --forceExit --runInBand env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} PROJECT_PATH: test-project TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None KUBE_STORAGE_CLASS: local-path PROVIDER_STRATEGY: aws AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_ENDPOINT: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566 GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}Apply the same change to all remaining test steps in the localstack job.
274-292: Add AWS_S3_FORCE_PATH_STYLE and AWS_EC2_METADATA_DISABLED to all LocalStack test steps.The k8s job (lines 157–158, 197–198, 237–238) correctly includes these variables for LocalStack compatibility. All 10 test steps in the localstack job are missing them, leading to inconsistent behavior. Add to all test steps in the localstack job.
Starting with the first test step (cloud-runner-end2end-locking):
- name: Run cloud-runner-end2end-locking test timeout-minutes: 60 run: yarn run test "cloud-runner-end2end-locking" --detectOpenHandles --forceExit --runInBand env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} PROJECT_PATH: test-project TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None KUBE_STORAGE_CLASS: local-path PROVIDER_STRATEGY: aws AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_ENDPOINT: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566 + AWS_S3_FORCE_PATH_STYLE: 'true' + AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}Apply the same additions (
AWS_S3_FORCE_PATH_STYLE: 'true'andAWS_EC2_METADATA_DISABLED: 'true') to the env blocks of all remaining test steps in the localstack job (lines 298–316, 322–340, 346–364, 370–388, 394–412, 418–436, 442–460, 466–484, 490–508).
135-160: Replace GITHUB_TOKEN override with default job token (k8s job: first test).Setting
GITHUB_TOKENto${{ secrets.GIT_PRIVATE_TOKEN }}overrides the runner-provided token and exposes a broader-scope PAT. KeepGIT_PRIVATE_TOKENseparate if private-repo access is needed; let the code's fallback logic handle it.Apply this diff:
- name: Run cloud-runner-end2end-caching test timeout-minutes: 60 run: yarn run test "cloud-runner-end2end-caching" --detectOpenHandles --forceExit --runInBand env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} PROJECT_PATH: test-project TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None KUBE_STORAGE_CLASS: local-path PROVIDER_STRATEGY: k8s containerCpu: '512' containerMemory: '512' AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_S3_ENDPOINT: http://localhost:4566 AWS_ENDPOINT: http://localhost:4566 INPUT_AWSS3ENDPOINT: http://localhost:4566 INPUT_AWSENDPOINT: http://localhost:4566 AWS_S3_FORCE_PATH_STYLE: 'true' AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}
176-200: Replace GITHUB_TOKEN override in retaining test (k8s job).Same token concern as caching test. Use the default job token instead.
Apply this diff:
- name: Run cloud-runner-end2end-retaining test timeout-minutes: 60 run: yarn run test "cloud-runner-end2end-retaining" --detectOpenHandles --forceExit --runInBand env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} PROJECT_PATH: test-project TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None KUBE_STORAGE_CLASS: local-path PROVIDER_STRATEGY: k8s containerCpu: '512' containerMemory: '512' AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_S3_ENDPOINT: http://localhost:4566 AWS_ENDPOINT: http://localhost:4566 INPUT_AWSS3ENDPOINT: http://localhost:4566 INPUT_AWSENDPOINT: http://localhost:4566 AWS_S3_FORCE_PATH_STYLE: 'true' AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}
216-240: Replace GITHUB_TOKEN override in hooks test (k8s job).Same token concern. Use the default job token instead.
Apply this diff:
- name: Run cloud-runner-hooks test timeout-minutes: 60 run: yarn run test "cloud-runner-hooks" --detectOpenHandles --forceExit --runInBand env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} PROJECT_PATH: test-project TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None KUBE_STORAGE_CLASS: local-path PROVIDER_STRATEGY: k8s containerCpu: '512' containerMemory: '512' AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_S3_ENDPOINT: http://localhost:4566 AWS_ENDPOINT: http://localhost:4566 INPUT_AWSS3ENDPOINT: http://localhost:4566 INPUT_AWSENDPOINT: http://localhost:4566 AWS_S3_FORCE_PATH_STYLE: 'true' AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}
70-73: Add region-aware S3 bucket creation for LocalStack.S3 requires
LocationConstraintoutsideus-east-1. The current code doesn't account for this and will silently fail.Apply this diff:
- name: Create S3 bucket for tests (host LocalStack) run: | - awslocal s3 mb s3://$AWS_STACK_NAME || aws --endpoint-url=http://localhost:4566 s3 mb s3://$AWS_STACK_NAME || true + if [ "${AWS_REGION:-us-east-1}" = "us-east-1" ]; then + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" || aws --endpoint-url=http://localhost:4566 s3api create-bucket --bucket "$AWS_STACK_NAME" || true + else + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" --create-bucket-configuration LocationConstraint="${AWS_REGION}" || aws --endpoint-url=http://localhost:4566 s3api create-bucket --bucket "$AWS_STACK_NAME" --create-bucket-configuration LocationConstraint="${AWS_REGION}" || true + fi awslocal s3 ls || aws --endpoint-url=http://localhost:4566 s3 ls || echo "S3 bucket listing completed"
40-43: Pin k3d version and avoid curl | bash installation.Line 42 uses an unpinned install script, which poses a supply-chain risk. Replace with a pinned binary download:
- name: Install k3d run: | - curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + set -euo pipefail + K3D_VERSION=v5.8.3 + curl -fsSL -o /usr/local/bin/k3d "https://github.com/k3d-io/k3d/releases/download/${K3D_VERSION}/k3d-linux-amd64" + chmod +x /usr/local/bin/k3d k3d version | catConsider storing
K3D_VERSIONin the workflow env section for easier updates.src/model/cloud-runner/remote-client/caching.ts (1)
96-188: Command injection risk, over‑broad cleanup scope, fragile ENOSPC detection, and deadretrySucceededlogic.Several concerns in this block:
Command injection & unsafe interpolation
- Interpolated values like
cacheFolder,cacheArtifactName,compressionSuffix,cacheParent, andcacheRootare inserted directly into shell strings forfind,rm,tar,mkdir -p, etc. (e.g., Lines 100–107, 119, 125–127, 139–142, 147–149).- They ultimately come from CLI/options/environment and can contain spaces or shell metacharacters, making this a command‑injection risk. The current
.replace(' ', '')only strips the first space and doesn’t prevent other dangerous characters.Suggestion: Refactor this logic to avoid building raw shell command strings:
- Prefer Node’s
fs.promisesandpathfor deletes and directory cleanup instead offind/rm.- If you absolutely must call shell utilities, expose a
CloudRunnerSystem.runCommand(cmd: string, args: string[])(or similar) that usesspawn/execFilewithout going through a shell, and validate all user‑controlled inputs with a strict whitelist (e.g./^[A-Za-z0-9._-]+$/for artifact names and a vetted set of cache base directories).- At minimum, validate
cacheFolder/cacheParent/cacheRootagainst an allow‑listed base path before using them in destructive commands.Over‑broad cleanup scope → potential data loss
- Cleanup uses
cacheParent = path.dirname(cacheFolder)andcacheRoot = path.resolve(cacheParent, '..'), then runsfind ${cacheParent} ... -deleteandfind ${cacheRoot} ... -deleteon"*.tar*"(Lines 100–107, 139–142, 144–149).- If
cacheFolderis relative (e.g.,'cache'),cacheParentmay be'.'andcacheRootmay be the project root, so you may end up deleting any.tar*file in the working directory and its parent, not just cache artifacts.- This is the same “overly broad cleanup scope” concern raised in the previous review and remains unresolved.
Suggestion: Restrict deletion strictly to the configured cache directory tree and the expected filename pattern:
- Use
cacheFolder(not its parent) as the search root.- Match a more specific pattern like
"${cacheArtifactName}.tar${compressionSuffix}"or a well‑defined prefix (e.g.,unity-cache-*.tar${compressionSuffix}), not all*.tar*.- Consider a configurable retention policy with a clear cap on the number/age of cache entries.
Fragile disk‑space error detection
- The retry path checks only
errorMessage.includes('No space left') || errorMessage.includes('Wrote only')(Line 131). Different platforms/tar implementations may use different messages, so real ENOSPC errors could slip through or be misclassified.Suggestion: Make the detection more robust by also checking standard error codes/errno:
- Include checks like
error?.code === 'ENOSPC'orerrorMessage.includes('ENOSPC')and (if available) process exit codes, in addition to the current string patterns.Dead / unnecessary
retrySucceededflag
retrySucceededis initialized tofalse, set totrueif the retry succeeds, and the retry’s catch unconditionally throws (Lines 153–166).- This means the
if (!retrySucceeded) { throw error; }branch (Lines 168–170) is effectively unreachable: either the retry succeeds (flag true, condition false) or the retry throws (you never reach theif).You can simplify this block:
- // Retry the tar operation once after cleanup - let retrySucceeded = false; - try { - await CloudRunnerSystem.Run( - `tar -cf ${cacheArtifactName}.tar${compressionSuffix} "${path.basename(sourceFolder)}"`, - ); - // If retry succeeds, mark it - we'll continue normally without throwing - retrySucceeded = true; - } catch (retryError: any) { - throw new Error( - `Failed to create cache archive after cleanup. Original error: ${errorMessage}. Retry error: ${ - retryError?.message || retryError - }`, - ); - } - // If retry succeeded, don't throw the original error - let execution continue after catch block - if (!retrySucceeded) { - throw error; - } - // If we get here, retry succeeded - execution will continue after the catch block + // Retry the tar operation once after cleanup + try { + await CloudRunnerSystem.Run( + `tar -cf ${cacheArtifactName}.tar${compressionSuffix} "${path.basename(sourceFolder)}"`, + ); + // If we get here, retry succeeded and execution will continue after the catch block + } catch (retryError: any) { + throw new Error( + `Failed to create cache archive after cleanup. Original error: ${errorMessage}. Retry error: ${ + retryError?.message || retryError + }`, + ); + }These issues were previously flagged in earlier review comments and still apply to the updated implementation.
🧹 Nitpick comments (1)
src/model/cloud-runner/remote-client/caching.ts (1)
82-95: Disk-usage parsing is fragile but acceptable; consider tightening.
df . 2>/dev/null || df /data ...plus/(\d+)%/works in many environments but is somewhat brittle (locale/format differences, multiple filesystems). Optional improvement: usedf -Pfor a more stable parse format and explicitly pick the last line’s%use(e.g., split by newline, then whitespace) instead of the first regex match.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (3)
.github/workflows/cloud-runner-integrity.yml(1 hunks)src/model/cloud-runner/providers/k8s/kubernetes-pods.ts(1 hunks)src/model/cloud-runner/remote-client/caching.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (LocalStack)
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (K8s)
- GitHub Check: Tests
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: Android on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: WSAPlayer on 2021.3.32f1
🔇 Additional comments (1)
src/model/cloud-runner/remote-client/caching.ts (1)
192-196: Good defensive check forcacheFolderbeforemv.Re‑ensuring
cacheFolderexists before moving the archive (in case cleanup removed empty directories) is a solid defensive addition and should prevent intermittentmvfailures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/cloud-runner-integrity.yml (2)
40-43: Unsafe k3d installation pattern remains; pin version instead.The workflow still uses
curl | bashto install k3d, which has supply-chain risk and poor reproducibility. A previous review flagged this (marked as "Addressed in commit 69731ba") but the unsafe pattern persists.Replace with a pinned release download:
- - name: Install k3d + - name: Install k3d (pinned) run: | + set -euo pipefail + K3D_VERSION=v5.7.3 - curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + mkdir -p /tmp/k3d && cd /tmp/k3d + curl -fsSL -o k3d "https://github.com/k3d-io/k3d/releases/download/${K3D_VERSION}/k3d-linux-amd64" + chmod +x k3d + sudo mv k3d /usr/local/bin/k3d k3d version | cat
164-165: Stop overriding GITHUB_TOKEN with GIT_PRIVATE_TOKEN across all test steps.The workflow sets
GITHUB_TOKENto${{ secrets.GIT_PRIVATE_TOKEN }}in 13 test steps across both jobs (k8s: 3 steps; localstack: 10 steps). This violates least-privilege by unnecessarily exposing broader-scope PATs and overrides GitHub's automatically-provided workflow token.Replace all occurrences with:
- GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}Keep
GIT_PRIVATE_TOKENas a separate variable if private repo access is truly required.Locations: 164-165, 204-205, 244-245, 296-297, 320-321, 344-345, 368-369, 392-393, 416-417, 440-441, 464-465, 488-489, 512-513
🧹 Nitpick comments (4)
.github/workflows/cloud-runner-integrity.yml (2)
73-76: S3 bucket creation should use region-aware API for robustness.The current approach uses
awslocal s3 mbwith an AWS CLI fallback, which doesn't handle region-aware bucket creation. For regions other thanus-east-1, S3 requires a LocationConstraint in the CreateBucket request.Replace with region-aware logic:
- awslocal s3 mb s3://$AWS_STACK_NAME || aws --endpoint-url=http://localhost:4566 s3 mb s3://$AWS_STACK_NAME || true - awslocal s3 ls || aws --endpoint-url=http://localhost:4566 s3 ls || echo "S3 bucket listing completed" + # Use s3api with region-aware LocationConstraint + REGION=${AWS_REGION:-us-east-1} + if [ "$REGION" = "us-east-1" ]; then + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" 2>/dev/null || true + else + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" \ + --create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null || true + fi + awslocal s3 ls || echo "S3 bucket listing completed"
118-139: Consider extracting repeated K8s cleanup logic into a reusable step or composite action.The K8s job repeats the cleanup logic (delete jobs, pods, PVCs, secrets, cache, prune Docker) three times verbatim. This creates maintenance burden—changes must be made in three places.
Consider extracting to a composite action or parameterized step for maintainability:
- name: Clean up K8s resources and disk uses: ./.github/actions/cleanup-k8s-test-resources with: namespace: defaultFor now, this is a minor maintainability observation; functionality is correct.
Also applies to: 166-180, 206-220
src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts (2)
66-66: Termination grace vs preStop hook look consistent; preStop command is dense but OKSetting
terminationGracePeriodSeconds: 90matches the preStop hook doing asleep 60plus a short license‑return script, so the pod should have enough time for the hook to complete before Kubernetes force‑kills it. That alignment looks good.The preStop shell command itself is quite compact and uses
&&/||chaining, which is correct but a bit hard to read/maintain. If you touch this again, consider extracting the full command string into a named constant or slightly simplifying it (e.g., dropping thecdif/steps/return_license.shis always absolute), but there’s no immediate functional issue here.Also applies to: 120-130
25-49: Reconsider hard-coded k3d endpoint rewriting without cluster detectionThe AWS endpoint rewriting for k3d is sensible for the local dev case, but the implementation has one concern:
- The
host.k3d.internalmapping is applied unconditionally to AWS endpoint vars whenever localhost URLs are detected. However, line 138 shows that the code can detectCLOUD_RUNNER_MINIKUBEenvironments. Other clusters (minikube, kind, EKS) use different gateway hostnames or may not need this rewrite at all. Consider either:
- Adding a similar detection flag for k3d and gating the rewrite logic
- Making the gateway hostname configurable (e.g.,
CLOUD_RUNNER_HOST_GATEWAYenv var)The object literal mapping on line 48 is fine as-is;
CloudRunnerEnvironmentVariablehas onlynameandvalueproperties, so no fields are being dropped.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (2)
.github/workflows/cloud-runner-integrity.yml(1 hunks)src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: Tests
- GitHub Check: WebGL on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2022.3.13f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: WSAPlayer on 2021.3.32f1
- GitHub Check: Android on 2021.3.32f1
🔇 Additional comments (1)
.github/workflows/cloud-runner-integrity.yml (1)
12-16: Minor: Consider re-adding id-token: write if OIDC operations are needed.The permissions were correctly tightened per prior feedback, removing
actions: writeandpull-requests: write. However, the original suggestion includedid-token: write. If this workflow uses OIDC or assume-role operations, restore it. If not needed, this is fine.
| AWS_ENDPOINT: http://localhost:4566 | ||
| AWS_ENDPOINT_URL: http://localhost:4566 | ||
| GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} | ||
| GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add AWS_S3_FORCE_PATH_STYLE and AWS_EC2_METADATA_DISABLED to all LocalStack job test steps.
The k8s job correctly includes AWS_S3_FORCE_PATH_STYLE: 'true' and AWS_EC2_METADATA_DISABLED: 'true' for LocalStack compatibility, but the localstack job omits these across all 10 test steps. This creates inconsistent behavior and may cause unexpected LocalStack interactions.
Add these variables to every test step in the localstack job. For example, lines 294–297:
AWS_ENDPOINT: http://localhost:4566
AWS_ENDPOINT_URL: http://localhost:4566
+ AWS_S3_FORCE_PATH_STYLE: 'true'
+ AWS_EC2_METADATA_DISABLED: 'true'
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}Apply the same additions to all other test steps in the localstack job (lines 318–321, 342–345, 366–369, 390–393, 414–417, 438–441, 462–465, 486–489, 510–513).
Also applies to: 318-321, 342-345, 366-369, 390-393, 414-417, 438-441, 462-465, 486-489, 510-513
🤖 Prompt for AI Agents
.github/workflows/cloud-runner-integrity.yml around lines 294-297 (and similarly
at 318-321, 342-345, 366-369, 390-393, 414-417, 438-441, 462-465, 486-489,
510-513): the localstack job's test steps are missing LocalStack-specific env
vars; add AWS_S3_FORCE_PATH_STYLE: 'true' and AWS_EC2_METADATA_DISABLED: 'true'
to the environment block of every test step in this localstack job so they match
the k8s job and ensure consistent LocalStack behavior across all listed steps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
src/model/cloud-runner/providers/k8s/kubernetes-pods.ts (3)
13-19: Wrap events fetch in try-catch to prevent masking pod failures.If
listNamespacedEventfails (e.g., RBAC restrictions, API timeout), the exception will propagate and obscure the actual pod failure. Since events are diagnostic-only, a failure here shouldn't prevent reporting the pod's failed state.- const events = (await kubeClient.listNamespacedEvent(namespace)).body.items - .filter((x) => x.involvedObject?.name === podName) - .map((x) => ({ - message: x.message || '', - reason: x.reason || '', - type: x.type || '', - })); + let events: Array<{ message: string; reason: string; type: string }> = []; + try { + events = (await kubeClient.listNamespacedEvent(namespace)).body.items + .filter((x) => x.involvedObject?.name === podName) + .map((x) => ({ + message: x.message || '', + reason: x.reason || '', + type: x.type || '', + })); + } catch (eventError) { + CloudRunnerLogger.logWarning(`Failed to fetch pod events: ${eventError}`); + }
35-58: Multi-container logic flaw: only the last container's exit status is retained.The loop iterates over all container statuses but overwrites
containerExitCodeandcontainerSucceededon each iteration (lines 47-49). If a pod has multiple containers and the last one succeeded but an earlier one failed, the code will incorrectly treat the pod as successful and skip error throwing at line 159.Consider checking if all containers succeeded:
- let containerExitCode: number | undefined; - let containerSucceeded = false; + let allContainersSucceeded = true; + let anyContainerExitCode: number | undefined; if (containerStatuses.length > 0) { containerStatuses.forEach((cs, idx) => { if (cs.state?.waiting) { errorDetails.push( `Container ${idx} (${cs.name}) waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message || ''}`, ); } if (cs.state?.terminated) { const exitCode = cs.state.terminated.exitCode; - containerExitCode = exitCode; + anyContainerExitCode = exitCode; if (exitCode === 0) { - containerSucceeded = true; + // Keep checking all containers + } else { + allContainersSucceeded = false; } errorDetails.push( `Container ${idx} (${cs.name}) terminated: ${cs.state.terminated.reason} - ${ cs.state.terminated.message || '' } (exit code: ${exitCode})`, ); } }); }Then update the logic at lines 71, 89, 126, 138, and 148 to use
allContainersSucceededandanyContainerExitCode.
89-123: Retry logic only checks the first container, inconsistent with multi-container handling.Line 101 explicitly accesses
updatedPod.status.containerStatuses[0], checking only the first container. This is inconsistent with the earlier loop (lines 39-57) that iterates over all containers, and assumes single-container pods.Issue: In multi-container pods, if the first container succeeds (exit code 0) but another container fails, the retry logic at line 108 will return
false, incorrectly treating the pod as successful.Consider iterating over all container statuses in the retry logic:
const updatedPod = ( await kubeClient.listNamespacedPod(namespace) ).body.items.find((x) => podName === x.metadata?.name); if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) { - const updatedContainerStatus = updatedPod.status.containerStatuses[0]; - if (updatedContainerStatus.state?.terminated) { - const updatedExitCode = updatedContainerStatus.state.terminated.exitCode; - if (updatedExitCode === 0) { - CloudRunnerLogger.logWarning( - `Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`, - ); - return false; // Pod is not running, but container succeeded - } else { - CloudRunnerLogger.log( - `Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`, - ); - errorDetails.push(`Container terminated after wait: exit code ${updatedExitCode}`); - containerExitCode = updatedExitCode; - containerSucceeded = false; - break; + let allSucceeded = true; + let anyTerminated = false; + for (const cs of updatedPod.status.containerStatuses) { + if (cs.state?.terminated) { + anyTerminated = true; + const exitCode = cs.state.terminated.exitCode; + if (exitCode !== 0) { + allSucceeded = false; + errorDetails.push( + `Container (${cs.name}) terminated after wait: exit code ${exitCode}`, + ); + } } } + if (anyTerminated) { + if (allSucceeded) { + CloudRunnerLogger.logWarning( + `Pod ${podName} all containers succeeded after waiting. PreStopHook failure is non-fatal.`, + ); + return false; + } + break; // At least one container failed + } }.github/workflows/cloud-runner-integrity.yml (3)
48-51: Use region-aware S3 API to support non-us-east-1 regions.S3 requires
LocationConstraintoutside us-east-1. The currentawslocal s3 mbcommand will fail for non-us-east-1 regions. This was flagged in prior feedback as accepted but not yet applied.Apply this diff to support all AWS regions:
- name: Create S3 bucket for tests (host LocalStack) run: | + if [ "${AWS_REGION:-us-east-1}" = "us-east-1" ]; then + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" || true + else + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" \ + --create-bucket-configuration LocationConstraint="${AWS_REGION}" || true + fi - awslocal s3 mb s3://$AWS_STACK_NAME || true awslocal s3 ls
252-267: Add AWS_S3_FORCE_PATH_STYLE and AWS_EC2_METADATA_DISABLED to all 10 LocalStack test steps.The k8s job correctly includes
AWS_S3_FORCE_PATH_STYLE: 'true'andAWS_EC2_METADATA_DISABLED: 'true', but all 10 test steps in the localstack job omit these required environment variables. This creates inconsistent LocalStack behavior between jobs.Add these variables to every test step's
envblock in the localstack job:PROVIDER_STRATEGY: aws AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_ENDPOINT: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566 + AWS_S3_FORCE_PATH_STYLE: 'true' + AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}Apply to: cloud-runner-end2end-locking, cloud-runner-end2end-caching, cloud-runner-end2end-retaining, cloud-runner-caching, cloud-runner-environment, cloud-runner-image, cloud-runner-hooks, cloud-runner-local-persistence, cloud-runner-locking-core, and cloud-runner-locking-get-locked tests.
40-43: Pin k3d version and avoid curl | bash to reduce supply-chain risk.The workflow currently downloads and executes an unpinned install script from the main branch, which is unverifiable and poses a supply-chain risk. The official k3d documentation recommends downloading the binary directly instead.
Apply this diff to pin the version and download the binary with checksum verification:
- - name: Install k3d + - name: Install k3d (pinned) run: | + set -euo pipefail + K3D_VERSION=v5.7.3 + curl -fsSL -o /usr/local/bin/k3d "https://github.com/k3d-io/k3d/releases/download/${K3D_VERSION}/k3d-linux-amd64" + chmod +x /usr/local/bin/k3d - curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash k3d version | cat
🧹 Nitpick comments (1)
.github/workflows/cloud-runner-integrity.yml (1)
88-110: Extract repetitive K8s cleanup logic into a reusable step or helper.The K8s cleanup logic is duplicated verbatim across three cleanup steps (lines 88–110, 136–150, 176–190). This violates DRY and makes future maintenance harder.
Consider extracting this into a composite action or a separate shell script that can be sourced. Example using a composite action:
- name: Clean up K8s test resources uses: ./.github/actions/k8s-cleanupThen create
.github/actions/k8s-cleanup/action.ymlwith the shared cleanup logic.Also applies to: 136-150, 176-190
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (2)
.github/workflows/cloud-runner-integrity.yml(1 hunks)src/model/cloud-runner/providers/k8s/kubernetes-pods.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (LocalStack)
- GitHub Check: Cloud Runner Integrity / Cloud Runner Tests (K8s)
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: Android on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
| GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} | ||
| GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stop overriding GITHUB_TOKEN with GIT_PRIVATE_TOKEN across 13 test steps.
The workflow sets GITHUB_TOKEN to ${{ secrets.GIT_PRIVATE_TOKEN }} in all test steps (k8s job: lines 135, 175, 215; localstack job: lines 267, 291, 315, 339, 363, 387, 411, 435, 459, 483). This:
- Exposes a broader-scope PAT unnecessarily to each job
- Breaks the intended fallback pattern in the code (try
GIT_PRIVATE_TOKENfirst, then fall back toGITHUB_TOKEN) - Violates principle of least privilege
Replace the pattern across all test steps to use the default runner-provided token:
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
+ GITHUB_TOKEN: ${{ github.token }}Keep GIT_PRIVATE_TOKEN as a separate variable if private repo access is required; the code has proper fallback logic to use it.
Also applies to: 175-175, 215-215, 267-267, 291-291, 315-315, 339-339, 363-363, 387-387, 411-411, 435-435, 459-459, 483-483
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
.github/workflows/cloud-runner-integrity.yml (4)
40-43: Replace unsafecurl | bashwith pinned k3d release.The workflow still uses an unpinned
curl | bashinvocation, which poses supply-chain risk and reproducibility concerns. This was flagged in previous reviews.Apply this diff to download a specific k3d release:
- - name: Install k3d + - name: Install k3d (pinned) if: ${{ matrix.provider == 'k8s' }} run: | - curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + set -euo pipefail + K3D_VERSION=v5.7.3 + curl -fsSL -o /usr/local/bin/k3d "https://github.com/k3d-io/k3d/releases/download/${K3D_VERSION}/k3d-linux-amd64" + chmod +x /usr/local/bin/k3d k3d version | cat
48-51: Use region-aware S3 bucket creation for LocalStack.The workflow creates the S3 bucket without specifying a region, which fails for non-us-east-1 deployments. S3 requires
LocationConstraintoutside us-east-1. This was flagged in previous reviews.Apply this diff to handle region-aware bucket creation:
- name: Create S3 bucket for tests (host LocalStack) run: | - awslocal s3 mb s3://$AWS_STACK_NAME || true + if [ "${AWS_REGION:-us-east-1}" = "us-east-1" ]; then + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" || true + else + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" \ + --create-bucket-configuration LocationConstraint="${AWS_REGION}" || true + fi awslocal s3 lsEnsure
AWS_REGIONis set in theenvblock (currently lines 17–25), or default tous-east-1if not set.
147-147: Stop overridingGITHUB_TOKENwithGIT_PRIVATE_TOKENacross 13 test steps.The workflow sets
GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}in all test steps, which:
- Exposes a broader-scope PAT unnecessarily to each job
- Breaks the intended fallback pattern in the code (try
GIT_PRIVATE_TOKENfirst, then fall back toGITHUB_TOKEN)- Violates the principle of least privilege
This was flagged in previous reviews and remains unaddressed.
Replace the pattern across all test steps to use the default runner-provided token:
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}Keep
GIT_PRIVATE_TOKENas a separate variable if private repo access is required; the code has proper fallback logic to use it.Also applies to: 187-187, 227-227, 279-279, 303-303, 327-327, 351-351, 375-375, 399-399, 423-423, 447-447, 471-471, 495-495
276-279: AddAWS_S3_FORCE_PATH_STYLEandAWS_EC2_METADATA_DISABLEDto all LocalStack test steps.The k8s job correctly includes
AWS_S3_FORCE_PATH_STYLE: 'true'andAWS_EC2_METADATA_DISABLED: 'true'(lines 144–145, 184–185, 224–225) for LocalStack compatibility, but the localstack job omits these across all 10 test steps. This creates inconsistent behavior and was flagged in previous reviews.Add these variables to every test step in the localstack job. For example, lines 276–279 should become:
AWS_ENDPOINT: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566 + AWS_S3_FORCE_PATH_STYLE: 'true' + AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}Apply the same additions to all other test steps in the localstack job (lines 300–303, 324–327, 348–351, 372–375, 396–399, 420–423, 444–447, 468–471, 492–495).
Also applies to: 300-303, 324-327, 348-351, 372-375, 396-399, 420-423, 444-447, 468-471, 492-495
🧹 Nitpick comments (3)
.github/workflows/cloud-runner-integrity.yml (1)
74-81: Remove duplicate cleanup code in k8s job.Lines 74–81 duplicate the cleanup logic from lines 74–81 (disk cleanup and docker system prune). Consolidate or remove the redundant block to improve readability.
- # Clean up disk space on the k3d node to prevent evictions - echo "Cleaning up disk space on k3d nodes..." - docker exec k3d-unity-builder-agent-0 sh -c "df -h && docker system prune -af --volumes || true" || true - docker system prune -af --volumes || true - # Clean up disk space on the node to prevent evictions - echo "Cleaning up disk space on k3d nodes..." - docker exec k3d-unity-builder-agent-0 sh -c "df -h && docker system prune -af --volumes || true" || true - docker system prune -af --volumes || truesrc/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts (2)
56-65: Consider clarifying the fallback logic and using a constant for error matching.The fallback recovery path is well-designed, but the condition at line 57 is complex. Consider:
- Adding a comment explaining that both
isRunning(pre-error) andcontinueStreaming(post-error) are checked to ensure the pod wasn't running before and after the error occurred.- Extracting the error message string
'previous terminated container'to a named constant to improve maintainability and make the error detection more explicit.Example refactor:
+ // kubectl error message when previous container logs are unavailable + static readonly PREVIOUS_CONTAINER_NOT_FOUND = 'previous terminated container'; + static async runTask( ... ) { ... } catch (error: any) { await new Promise((resolve) => setTimeout(resolve, 3000)); const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient); CloudRunnerLogger.log(`K8s logging error ${error} ${continueStreaming}`); - // If pod is not running and we tried --previous but it failed, try without --previous - if (!isRunning && !continueStreaming && error?.message?.includes('previous terminated container')) { + // If pod wasn't running initially and still isn't running after error, + // and we got a "previous container not found" error, try fetching current container logs + if (!isRunning && !continueStreaming && error?.message?.includes(KubernetesTaskRunner.PREVIOUS_CONTAINER_NOT_FOUND)) {
74-78: Verify non-fatal handling is appropriate for all scenarios.Treating the "previous terminated container" error as non-fatal improves resilience, but ensure this behavior is acceptable for all use cases where previous container logs might be critical for debugging.
Also consider using the same constant suggested in the previous comment for consistency.
- if (error?.message?.includes('previous terminated container')) { + if (error?.message?.includes(KubernetesTaskRunner.PREVIOUS_CONTAINER_NOT_FOUND)) { CloudRunnerLogger.logWarning(`Could not fetch previous container logs, but continuing...`); break; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (3)
.github/workflows/cloud-runner-integrity.yml(1 hunks)src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts(5 hunks)src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: WebGL on 2021.3.32f1
- GitHub Check: iOS on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
🔇 Additional comments (2)
.github/workflows/cloud-runner-integrity.yml (1)
513-513: Update deprecatedaws-actions/configure-aws-credentials@v1to v4 in commented AWS job.Although the AWS job is currently commented out (line 496), the action reference at line 513 uses the deprecated v1, which will fail on current runners if/when the job is enabled.
Apply this diff to future-proof the commented job:
- name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts (1)
31-35: Good optimization: caching pod status check.Caching the
IsPodRunningresult improves clarity and avoids redundant checks when determining log flags. The separate check at line 54 after an error is appropriate since pod state may have changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts (1)
55-90: Fallback and retry logic looks robust; consider clarifying retry counter semanticsThe new fallback when previous‑container logs are unavailable, plus the special‑case handling of
"previous terminated container"errors, makes the log streaming much more resilient and avoids failing builds just because prior container logs are missing.At the same time,
retriesAfterFinishis now used as a general retry counter for several cases (fallback attempts, generic log errors, and “no end marker” scenarios), not only “after finish”. The name is a bit misleading and makes it harder to reason about when the loop will actually stop.To improve readability and future maintenance, consider:
- Renaming
retriesAfterFinishto something more generic likelogRetryCountorlogErrorRetries.- Adding a short comment near its declaration documenting that it governs both fallback and general log‑error retries, and that exceeding
maxRetryfor"previous terminated container"errors is treated as non‑fatal, while other errors are re‑thrown.This doesn’t change behavior but makes the control flow easier to understand for future contributors.
Also applies to: 100-107
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (1)
src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts (3)
src/model/cloud-runner/services/core/cloud-runner-logger.ts (1)
error(24-26)src/model/cloud-runner/services/core/cloud-runner-system.ts (1)
CloudRunnerSystem(4-69)src/model/cloud-runner/services/core/follow-log-stream-service.ts (1)
FollowLogStreamService(7-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: WebGL on 2021.3.32f1
- GitHub Check: iOS on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: tvOS on 2021.3.32f1
| const isRunning = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient); | ||
| let extraFlags = ``; | ||
| extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient)) | ||
| ? ` -f -c ${containerName} -n ${namespace}` | ||
| : ` --previous -n ${namespace}`; | ||
| extraFlags += isRunning ? ` -f -c ${containerName} -n ${namespace}` : ` --previous -n ${namespace}`; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing -c flag for --previous logs can break multi‑container pods
In the non‑running case, extraFlags does not specify the container name:
const isRunning = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
let extraFlags = ``;
extraFlags += isRunning ? ` -f -c ${containerName} -n ${namespace}` : ` --previous -n ${namespace}`;For pods with multiple containers, kubectl logs <pod> --previous without -c may either fail or select the wrong container, even though containerName is available.
Consider including -c ${containerName} in the --previous branch for consistency and correctness:
- extraFlags += isRunning ? ` -f -c ${containerName} -n ${namespace}` : ` --previous -n ${namespace}`;
+ extraFlags += isRunning
+ ? ` -f -c ${containerName} -n ${namespace}`
+ : ` --previous -c ${containerName} -n ${namespace}`;🤖 Prompt for AI Agents
In src/model/cloud-runner/providers/k8s/kubernetes-task-runner.ts around lines
31 to 34, the branch that builds extraFlags for non-running pods omits the -c
container flag which can break multi-container pods; change the ternary so both
branches include `-c ${containerName}` (i.e., use ` -f -c ${containerName} -n
${namespace}` for running and ` --previous -c ${containerName} -n ${namespace}`
for non‑running) to ensure the correct container is selected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (3)
src/model/cloud-runner/remote-client/index.ts (2)
282-282: Fix contradictory LFS folder assertion as previously flagged.The assertion checks that the LFS folder exists (
fs.existsSync()returns true), but the message states it "should not exist before caching." This contradiction was flagged in a previous review.Either invert the check to
!fs.existsSync(path.join('.git', 'lfs'))or update the message to match the actual assertion.
242-268: Fix unused catch bindings as previously flagged.This issue was already identified in a previous review. Remove unused catch bindings (
_error,_error2,_error3) and use parameterlesscatchblocks where the error is not used, or name the caught error appropriately when it is thrown.Based on past review comments, the recommended fix is to use
catchwithout parameters for unused errors, and preserve the error variable where it's re-thrown.src/model/cloud-runner/workflows/build-automation-workflow.ts (1)
201-206: Non-containerized providers do not execute the build.The non-containerized code path never copies the build scripts or executes
/entrypoint.sh, so no actual Unity build occurs. The workflow only logs markers and runs post-build tasks against non-existent build outputs.This confirms the issue flagged in the previous review: "Local provider no longer executes build entrypoint... local runs will immediately log success without invoking any build logic, so local provider builds will silently do nothing."
The non-containerized path should stage and execute the build scripts similar to the containerized paths. Apply this structure:
return ` echo "game ci start" echo "game ci start" >> "$LOG_FILE" + mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`} + cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)} + cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction" + cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'entrypoint.sh'))}" "/entrypoint.sh" + cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'steps'))}" "/steps" + chmod -R +x "/entrypoint.sh" + chmod -R +x "/steps" + /entrypoint.sh 2>&1 | tee -a "$LOG_FILE" & timeout 3s node ${builderPath} -m remote-cli-log-stream --logFile "$LOG_FILE" || true node ${builderPath} -m remote-cli-post-build`;
🧹 Nitpick comments (2)
src/model/cloud-runner/remote-client/index.ts (1)
128-134: Consider consolidating logging calls.The success message is logged three separate ways (CloudRunnerLogger, stdout, console). If CloudRunnerLogger already outputs to stdout/console, this may be redundant.
src/model/cloud-runner/workflows/build-automation-workflow.ts (1)
95-106: Consider preserving git error output for troubleshooting.The multi-tier fallback strategy (checking remote branch, then falling back to known branches) is well-designed. However, redirecting stderr to
/dev/nullon line 98 may hide authentication failures or network issues that would help diagnose clone problems.Consider logging git errors while still allowing the fallback chain to proceed:
-if [ -n "$(git ls-remote --heads \"$REPO\" \"$BRANCH\" 2>/dev/null)" ]; then +if git ls-remote --heads "$REPO" "$BRANCH" 2>&1 | grep -q "refs/heads/$BRANCH"; then git clone -q -b "$BRANCH" "$REPO" "$DEST" else echo "Remote branch $BRANCH not found in $REPO; falling back to a known branch" - git clone -q -b cloud-runner-develop "$REPO" "$DEST" \ - || git clone -q -b main "$REPO" "$DEST" \ - || git clone -q "$REPO" "$DEST" + git clone -q -b cloud-runner-develop "$REPO" "$DEST" 2>&1 \ + || git clone -q -b main "$REPO" "$DEST" 2>&1 \ + || git clone -q "$REPO" "$DEST" 2>&1 \ + || { echo "ERROR: All git clone attempts failed for $REPO"; exit 1; } fi
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (2)
src/model/cloud-runner/remote-client/index.ts(3 hunks)src/model/cloud-runner/workflows/build-automation-workflow.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-30T18:01:50.339Z
Learnt from: MichaelBuhler
Repo: game-ci/unity-builder PR: 685
File: .github/workflows/build-tests-ubuntu.yml:146-146
Timestamp: 2025-01-30T18:01:50.339Z
Learning: The build steps duplication in the workflow is intentional to implement a retry mechanism with different delay intervals between attempts.
Applied to files:
src/model/cloud-runner/workflows/build-automation-workflow.ts
🧬 Code graph analysis (1)
src/model/cloud-runner/workflows/build-automation-workflow.ts (1)
src/model/index.ts (1)
CloudRunner(27-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: WSAPlayer on 2021.3.32f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: WebGL on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: iOS on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: iOS on 2021.3.45f1
🔇 Additional comments (9)
src/model/cloud-runner/remote-client/index.ts (4)
66-68: LGTM: Enhanced observability for cache key.The explicit cache key logging aids debugging and test assertions.
70-89: LGTM: Defensive cache push guards.The existence and content checks prevent pushing empty or missing directories, improving robustness.
91-110: LGTM: Consistent defensive pattern.Build cache push uses the same guarded pattern as Library cache, maintaining consistency.
113-120: LGTM: Safe cleanup guard.Checking both path variants before cleanup prevents spurious errors.
src/model/cloud-runner/workflows/build-automation-workflow.ts (5)
53-60: Container-aware path resolution looks correct.The
isContainerizedflag appropriately distinguishes between containerized providers (aws,k8s,local-docker) and non-containerized ones, with thebuilderPathcorrectly resolving to absolute paths for containers andprocess.cwd()for local execution.
64-82: Conditional environment setup is well-structured.The workflow correctly skips package installation for
local-docker(where the Docker image provides tooling) and non-containerized providers, while maintaining appropriate paths for workspace and log files based on the execution context.
189-198: Containerized build execution is correct.The non-local-docker containerized path properly stages the Unity build scripts, executes
/entrypoint.sh, and streams logs through the remote CLI. The build logic is intact for these providers.
150-161: Git LFS handling is appropriately defensive.The conditional Git LFS setup correctly checks for a
.gitdirectory before attempting LFS operations and gracefully handles environments where Git LFS is unavailable or unnecessary. The error tolerance (|| true) is appropriate since LFS is optional for many repositories.
182-185: Cache mirroring supports testing workflows.The cache mirroring from
/data/cacheback into the workspace enables test assertions to validate cache behavior in local-docker runs. The error tolerance is appropriate since this is a diagnostic aid rather than a critical build step.
| } else { | ||
| await CloudRunnerSystem.Run(`git checkout ${CloudRunner.buildParameters.branch}`); | ||
| try { | ||
| await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); | ||
| } catch (_error) { | ||
| if ((targetBranch || '').startsWith('pull/')) { | ||
| await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); | ||
| } else { | ||
| throw _error; | ||
| } | ||
| } | ||
| RemoteClientLogger.log(`buildParameter Git Sha is empty`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused catch binding.
Similar to the previous issue, line 272 uses an unused catch binding _error. Either remove the parameter (catch {) or name it error if it will be referenced.
🤖 Prompt for AI Agents
In src/model/cloud-runner/remote-client/index.ts around lines 269 to 279, the
catch block declares an unused binding `_error`; either remove the binding
(`catch { ... }`) since the error variable isn't used, or rename `_error` to
`error` and update the later `throw _error` to `throw error` so the thrown value
matches the binding. Make the minimal change (preferably `catch {`) and keep the
existing control flow that checks the branch name and rethrows when appropriate.
| if (CloudRunner.buildParameters.skipLfs) { | ||
| RemoteClientLogger.log(`Skipping LFS pull (skipLfs=true)`); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // Best effort: try plain pull first (works for public repos or pre-configured auth) | ||
| try { | ||
| await CloudRunnerSystem.Run(`git lfs pull`, true); | ||
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | ||
| RemoteClientLogger.log(`Pulled LFS files without explicit token configuration`); | ||
|
|
||
| return; | ||
| } catch (_error) { | ||
| /* no-op: best-effort git lfs pull without tokens may fail */ | ||
| void 0; | ||
| } | ||
|
|
||
| // Try with GIT_PRIVATE_TOKEN | ||
| try { | ||
| const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN; | ||
| if (gitPrivateToken) { | ||
| RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | ||
| await CloudRunnerSystem.Run( | ||
| `git config --global url."https://${gitPrivateToken}@github.com/".insteadOf "https://github.com/"`, | ||
| ); | ||
| await CloudRunnerSystem.Run(`git lfs pull`, true); | ||
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | ||
| RemoteClientLogger.log(`Successfully pulled LFS files with GIT_PRIVATE_TOKEN`); | ||
|
|
||
| return; | ||
| } | ||
| } catch (error: any) { | ||
| RemoteClientLogger.logCliError(`Failed with GIT_PRIVATE_TOKEN: ${error.message}`); | ||
| } | ||
|
|
||
| // Try with GITHUB_TOKEN | ||
| try { | ||
| const githubToken = process.env.GITHUB_TOKEN; | ||
| if (githubToken) { | ||
| RemoteClientLogger.log(`Attempting to pull LFS files with GITHUB_TOKEN fallback...`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | ||
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | ||
| await CloudRunnerSystem.Run( | ||
| `git config --global url."https://${githubToken}@github.com/".insteadOf "https://github.com/"`, | ||
| ); | ||
| await CloudRunnerSystem.Run(`git lfs pull`, true); | ||
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | ||
| RemoteClientLogger.log(`Successfully pulled LFS files with GITHUB_TOKEN`); | ||
|
|
||
| return; | ||
| } | ||
| } catch (error: any) { | ||
| RemoteClientLogger.logCliError(`Failed with GITHUB_TOKEN: ${error.message}`); | ||
| } | ||
|
|
||
| // If we get here, all strategies failed; continue without failing the build | ||
| RemoteClientLogger.logWarning(`Proceeding without LFS files (no tokens or pull failed)`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused catch binding and simplify no-op.
Lines 316 and 318: The catch binding _error is unused, and the void 0; statement is unconventional for a no-op. Use a parameterless catch block or add a brief comment without the void 0 statement.
Apply this pattern:
- } catch (_error) {
- /* no-op: best-effort git lfs pull without tokens may fail */
- void 0;
+ } catch {
+ // no-op: best-effort git lfs pull without tokens may fail
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (CloudRunner.buildParameters.skipLfs) { | |
| RemoteClientLogger.log(`Skipping LFS pull (skipLfs=true)`); | |
| return; | |
| } | |
| // Best effort: try plain pull first (works for public repos or pre-configured auth) | |
| try { | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Pulled LFS files without explicit token configuration`); | |
| return; | |
| } catch (_error) { | |
| /* no-op: best-effort git lfs pull without tokens may fail */ | |
| void 0; | |
| } | |
| // Try with GIT_PRIVATE_TOKEN | |
| try { | |
| const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN; | |
| if (gitPrivateToken) { | |
| RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | |
| await CloudRunnerSystem.Run( | |
| `git config --global url."https://${gitPrivateToken}@github.com/".insteadOf "https://github.com/"`, | |
| ); | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Successfully pulled LFS files with GIT_PRIVATE_TOKEN`); | |
| return; | |
| } | |
| } catch (error: any) { | |
| RemoteClientLogger.logCliError(`Failed with GIT_PRIVATE_TOKEN: ${error.message}`); | |
| } | |
| // Try with GITHUB_TOKEN | |
| try { | |
| const githubToken = process.env.GITHUB_TOKEN; | |
| if (githubToken) { | |
| RemoteClientLogger.log(`Attempting to pull LFS files with GITHUB_TOKEN fallback...`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | |
| await CloudRunnerSystem.Run( | |
| `git config --global url."https://${githubToken}@github.com/".insteadOf "https://github.com/"`, | |
| ); | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Successfully pulled LFS files with GITHUB_TOKEN`); | |
| return; | |
| } | |
| } catch (error: any) { | |
| RemoteClientLogger.logCliError(`Failed with GITHUB_TOKEN: ${error.message}`); | |
| } | |
| // If we get here, all strategies failed; continue without failing the build | |
| RemoteClientLogger.logWarning(`Proceeding without LFS files (no tokens or pull failed)`); | |
| if (CloudRunner.buildParameters.skipLfs) { | |
| RemoteClientLogger.log(`Skipping LFS pull (skipLfs=true)`); | |
| return; | |
| } | |
| // Best effort: try plain pull first (works for public repos or pre-configured auth) | |
| try { | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Pulled LFS files without explicit token configuration`); | |
| return; | |
| } catch { | |
| // no-op: best-effort git lfs pull without tokens may fail | |
| } | |
| // Try with GIT_PRIVATE_TOKEN | |
| try { | |
| const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN; | |
| if (gitPrivateToken) { | |
| RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | |
| await CloudRunnerSystem.Run( | |
| `git config --global url."https://${gitPrivateToken}@github.com/".insteadOf "https://github.com/"`, | |
| ); | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Successfully pulled LFS files with GIT_PRIVATE_TOKEN`); | |
| return; | |
| } | |
| } catch (error: any) { | |
| RemoteClientLogger.logCliError(`Failed with GIT_PRIVATE_TOKEN: ${error.message}`); | |
| } | |
| // Try with GITHUB_TOKEN | |
| try { | |
| const githubToken = process.env.GITHUB_TOKEN; | |
| if (githubToken) { | |
| RemoteClientLogger.log(`Attempting to pull LFS files with GITHUB_TOKEN fallback...`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."ssh://[email protected]/".insteadOf || true`); | |
| await CloudRunnerSystem.Run(`git config --global --unset-all url."[email protected]".insteadOf || true`); | |
| await CloudRunnerSystem.Run( | |
| `git config --global url."https://${githubToken}@github.com/".insteadOf "https://github.com/"`, | |
| ); | |
| await CloudRunnerSystem.Run(`git lfs pull`, true); | |
| await CloudRunnerSystem.Run(`git lfs checkout || true`, true); | |
| RemoteClientLogger.log(`Successfully pulled LFS files with GITHUB_TOKEN`); | |
| return; | |
| } | |
| } catch (error: any) { | |
| RemoteClientLogger.logCliError(`Failed with GITHUB_TOKEN: ${error.message}`); | |
| } | |
| // If we get here, all strategies failed; continue without failing the build | |
| RemoteClientLogger.logWarning(`Proceeding without LFS files (no tokens or pull failed)`); |
🤖 Prompt for AI Agents
In src/model/cloud-runner/remote-client/index.ts around lines 303 to 364, the
first try/catch uses an unused catch binding (`_error`) and an unconventional
`void 0;` no-op; replace the catch clause with a parameterless catch and remove
`void 0`, e.g. use `catch { /* no-op: best-effort git lfs pull may fail */ }`
(or an empty block) so the code is cleaner and ESLint-friendly while preserving
the original no-op behavior.
| const sha = CloudRunner.buildParameters.gitSha; | ||
| const branch = CloudRunner.buildParameters.branch; | ||
| try { | ||
| await CloudRunnerSystem.Run(`git reset --hard "${sha}"`); | ||
| await CloudRunnerSystem.Run(`git checkout ${sha}`); | ||
| } catch (_error) { | ||
| RemoteClientLogger.logWarning(`Retained workspace: SHA not found, falling back to branch ${branch}`); | ||
| try { | ||
| await CloudRunnerSystem.Run(`git checkout ${branch}`); | ||
| } catch (_error2) { | ||
| if ((branch || '').startsWith('pull/')) { | ||
| await CloudRunnerSystem.Run(`git checkout origin/${branch}`); | ||
| } else { | ||
| throw _error2; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused catch bindings in retained workspace logic.
Lines 391 and 395 have unused catch bindings _error and _error2. Use parameterless catch blocks where errors aren't referenced, or name them error when thrown.
🤖 Prompt for AI Agents
In src/model/cloud-runner/remote-client/index.ts around lines 386 to 402, the
two catch blocks declare unused bindings (_error and _error2); remove the unused
identifiers by turning those into parameterless catch blocks (catch { ... }) or,
if you intend to inspect the error, rename the bindings to a meaningful name
like error and use them; update the first catch to catch {
RemoteClientLogger.logWarning(...) ... } and the inner catch to catch { if
((branch || '').startsWith('pull/')) ... else throw; } (or name and use the
variables) so no unused catch parameters remain.
|
|
||
| return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1 | ||
| ${cloneBuilderCommands} | ||
| echo "log start" >> /home/job-log.txt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
rg -nP --type=ts 'LOG_FILE|job-log\.txt' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 1146
🏁 Script executed:
sed -n '75,90p' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 1110
🏁 Script executed:
sed -n '108,126p' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 923
🏁 Script executed:
sed -n '165,175p' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 1335
🏁 Script executed:
sed -n '190,210p' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 1354
Replace hardcoded log path with $LOG_FILE variable for consistency.
Line 119 should use $LOG_FILE instead of hardcoding /home/job-log.txt. Although the hardcoded path matches the containerized value set on line 82, the variable is already defined and should be used consistently throughout the script, as seen in lines 130, 204, and 205.
🤖 Prompt for AI Agents
In src/model/cloud-runner/workflows/build-automation-workflow.ts around line
119, the script echoes to the hardcoded path "/home/job-log.txt" instead of
using the existing $LOG_FILE variable; change that echo command to append to
"$LOG_FILE" so the script consistently uses the configured log path (matching
usage on lines 82, 130, 204, 205).
| return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1 | ||
| mkdir -p "$(dirname "$LOG_FILE")" | ||
| echo "log start" >> "$LOG_FILE" | ||
| echo "CACHE_KEY=$CACHE_KEY"`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cd src/model/cloud-runner/workflows && wc -l build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 97
🏁 Script executed:
cat -n src/model/cloud-runner/workflows/build-automation-workflow.ts | sed -n '80,140p'Repository: game-ci/unity-builder
Length of output: 3331
🏁 Script executed:
# Search for how builderPath is used
rg -n 'builderPath' src/model/cloud-runner/workflows/build-automation-workflow.ts -A 3 -B 1Repository: game-ci/unity-builder
Length of output: 3882
🏁 Script executed:
# Search for non-containerized provider strategy usage
rg -n 'providerStrategy.*local' src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -C 3Repository: game-ci/unity-builder
Length of output: 11083
🏁 Script executed:
# Look for initialization of builder in non-containerized scenarios
rg -n 'isContainerized\s*=\s*false|!isContainerized' src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -C 5Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Search for how "local" provider is configured and whether builder is pre-installed
rg -n "providerStrategy.*=.*['\"]local['\"]" src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -B 5 -A 10Repository: game-ci/unity-builder
Length of output: 3008
🏁 Script executed:
# Look for any documentation or setup instructions for local provider
fd -e md -e txt | xargs grep -l "local provider\|local strategy" 2>/dev/null | head -5Repository: game-ci/unity-builder
Length of output: 107
🏁 Script executed:
# Search for how the builder is expected to exist before setupCommands is called
rg -n "process\.cwd\(\)" src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -B 3 -A 3Repository: game-ci/unity-builder
Length of output: 6027
🏁 Script executed:
# Check if there's any initialization for non-containerized paths
rg -n "dist.*index\.js\|builderPath" src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -B 2 -A 2 | head -80Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
cat -n src/model/cloud-runner/providers/README.mdRepository: game-ci/unity-builder
Length of output: 8318
🏁 Script executed:
# Look for local provider implementation and initialization
fd "local" src/model/cloud-runner/providers/ -t f | head -10Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Search for where the builder dist folder might be copied/prepared for local provider
rg -n "local.*provider\|LocalProvider" src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -B 3 -A 10 | head -100Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Search for where the builder might be built/compiled before running in non-containerized mode
rg -n "npm run build\|npm run package\|dist/" src/ -t ts --type-add='ts:*.ts' | grep -E "local|setup|build" | head -40Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Check if there's a build step that creates dist/index.js before running workflows
rg -n "compile\|package\|build" src/index.ts src/main.ts -B 3 -A 3Repository: game-ci/unity-builder
Length of output: 116
🏁 Script executed:
# Look at how the workflow is actually executed - check the main entry point
fd "index.ts|main.ts" src/ -type f | head -5Repository: game-ci/unity-builder
Length of output: 235
🏁 Script executed:
# Find package.json to understand entry point and build setup
cat package.json | grep -A 5 -B 5 '"main"\|"bin"\|"dist"'Repository: game-ci/unity-builder
Length of output: 379
🏁 Script executed:
# Check if dist/index.js is pre-built or built at runtime
ls -la dist/ 2>/dev/null | head -20Repository: game-ci/unity-builder
Length of output: 867
🏁 Script executed:
# Look at the root index.ts or entry point
git ls-files | grep -E "^(src/)?index\.(ts|js)$|^main\.(ts|js)$"Repository: game-ci/unity-builder
Length of output: 77
🏁 Script executed:
# Check the actual structure of src directory
git ls-files src/ | head -20Repository: game-ci/unity-builder
Length of output: 666
🏁 Script executed:
# Check how local and local-system providers are actually used
rg -n "local-system\|providerStrategy.*local" src/model/cloud-runner/ -t ts --type-add='ts:*.ts' -B 2 -A 5 | head -80Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Verify if there's any documentation about how non-containerized mode works
fd -e md | xargs grep -l "local.*provider\|non-containerized" 2>/dev/nullRepository: game-ci/unity-builder
Length of output: 107
🏁 Script executed:
# Check the git history or comments around the setupCommands method
git log --oneline -n 20 -- src/model/cloud-runner/workflows/build-automation-workflow.ts 2>/dev/null | head -10Repository: game-ci/unity-builder
Length of output: 84
🏁 Script executed:
# Check if there are any comments or documentation about why non-containerized doesn't clone
rg -n "non-containerized\|isContainerized" src/model/cloud-runner/workflows/build-automation-workflow.ts -B 5 -A 2 | grep -E "//|/\*"Repository: game-ci/unity-builder
Length of output: 47
🏁 Script executed:
# Verify that node ${builderPath} commands are actually used in both paths
rg -n "node \${builderPath}" src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 173
🏁 Script executed:
# Search for node command with builderPath
rg -n 'node.*builderPath' src/model/cloud-runner/workflows/build-automation-workflow.tsRepository: game-ci/unity-builder
Length of output: 837
Non-containerized setup doesn't clone the builder repository while assuming it exists downstream.
The non-containerized code path (lines 128–131) only initializes logging but never clones the Unity builder repository. Since builderPath for non-containerized mode points to process.cwd()/dist/index.js, subsequent node ${builderPath} commands (lines 205–206) will fail unless the builder is already present in the working directory. This creates a critical inconsistency: containerized providers clone the builder (lines 108–126), while non-containerized providers do not, yet both expect the builder to exist.
Clarify whether non-containerized providers are expected to have the builder pre-installed, and if so, document this requirement. Otherwise, the non-containerized path should include clone logic similar to the containerized path.
| if ! command -v node > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/node && chmod +x /usr/local/bin/node; fi | ||
| if ! command -v npm > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/npm && chmod +x /usr/local/bin/npm; fi | ||
| if ! command -v n > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/n && chmod +x /usr/local/bin/n; fi | ||
| if ! command -v yarn > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/yarn && chmod +x /usr/local/bin/yarn; fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stub binaries may mask missing tool dependencies.
Creating no-op stubs for node, npm, n, and yarn when they're missing (lines 167-170) will cause any scripts that genuinely require these tools to silently fail. If the local-docker image is expected to provide these tools, their absence likely indicates a misconfiguration that should be diagnosed rather than hidden.
Consider at minimum logging a warning before creating stubs:
+echo "WARNING: Creating stub for missing 'node' command - build may fail if node is required"
if ! command -v node > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/node && chmod +x /usr/local/bin/node; fi
+echo "WARNING: Creating stub for missing 'npm' command - build may fail if npm is required"
if ! command -v npm > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/npm && chmod +x /usr/local/bin/npm; fi
+echo "WARNING: Creating stub for missing 'n' command - build may fail if n is required"
if ! command -v n > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/n && chmod +x /usr/local/bin/n; fi
+echo "WARNING: Creating stub for missing 'yarn' command - build may fail if yarn is required"
if ! command -v yarn > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/yarn && chmod +x /usr/local/bin/yarn; fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ! command -v node > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/node && chmod +x /usr/local/bin/node; fi | |
| if ! command -v npm > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/npm && chmod +x /usr/local/bin/npm; fi | |
| if ! command -v n > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/n && chmod +x /usr/local/bin/n; fi | |
| if ! command -v yarn > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/yarn && chmod +x /usr/local/bin/yarn; fi | |
| echo "WARNING: Creating stub for missing 'node' command - build may fail if node is required" | |
| if ! command -v node > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/node && chmod +x /usr/local/bin/node; fi | |
| echo "WARNING: Creating stub for missing 'npm' command - build may fail if npm is required" | |
| if ! command -v npm > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/npm && chmod +x /usr/local/bin/npm; fi | |
| echo "WARNING: Creating stub for missing 'n' command - build may fail if n is required" | |
| if ! command -v n > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/n && chmod +x /usr/local/bin/n; fi | |
| echo "WARNING: Creating stub for missing 'yarn' command - build may fail if yarn is required" | |
| if ! command -v yarn > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/yarn && chmod +x /usr/local/bin/yarn; fi |
🤖 Prompt for AI Agents
In src/model/cloud-runner/workflows/build-automation-workflow.ts around lines
167-170, the current behavior silently creates no-op stubs for node/npm/n/yarn
when those binaries are missing; change this to avoid masking missing tool
dependencies by (1) removing the silent no-op replacements and instead emitting
a clear warning/error that the expected tool is absent (log to stderr or your
workflow logger with the binary name and suggested fix), and (2) if stubs are
still required for a specific compatibility case, only create them after logging
the warning and documenting the condition in code comments; ensure the workflow
exits with a non-zero status or fails the job when a genuine runtime dependency
is missing unless an explicit opt-in flag allows stubbing.
| tar -cf "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar" --files-from /dev/null || touch "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar" | ||
| fi | ||
| # Run post-build tasks - ensure output is captured even if command fails | ||
| node ${builderPath} -m remote-cli-post-build || echo "Post-build command completed with warnings" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Post-build error suppression may hide critical failures.
The || echo "Post-build command completed with warnings" pattern (line 180) converts all post-build failures into a success state with a misleading message. If remote-cli-post-build fails while uploading artifacts or persisting cache, the build will appear to succeed despite incomplete outputs.
Consider either:
- Propagating the failure if post-build tasks are critical, or
- Logging the actual error and exit code:
-node ${builderPath} -m remote-cli-post-build || echo "Post-build command completed with warnings"
+node ${builderPath} -m remote-cli-post-build || { EXIT_CODE=$?; echo "WARNING: Post-build command failed with exit code $EXIT_CODE"; exit $EXIT_CODE; }Or if post-build failures are truly non-critical:
-node ${builderPath} -m remote-cli-post-build || echo "Post-build command completed with warnings"
+node ${builderPath} -m remote-cli-post-build || { EXIT_CODE=$?; echo "WARNING: Post-build command failed with exit code $EXIT_CODE, continuing anyway"; }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| node ${builderPath} -m remote-cli-post-build || echo "Post-build command completed with warnings" | |
| node ${builderPath} -m remote-cli-post-build || { EXIT_CODE=$?; echo "WARNING: Post-build command failed with exit code $EXIT_CODE"; exit $EXIT_CODE; } |
🤖 Prompt for AI Agents
In src/model/cloud-runner/workflows/build-automation-workflow.ts around line
180, the shell line `node ${builderPath} -m remote-cli-post-build || echo
"Post-build command completed with warnings"` hides failures; change it to
either let the command fail (remove the `||` fallback) if post-build steps are
critical, or capture the exit code and log the error and exit code before
exiting non-zero (e.g., run the command, check `$?`, log context and exit with
that code), or — only if truly non-critical — replace the echo with a detailed
warning that includes the captured exit code and stderr output so failures are
visible for debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
.github/workflows/cloud-runner-integrity.yml (4)
56-59: Use region-aware S3 API for bucket creation.The past review suggested using
s3api create-bucketwithLocationConstraintfor non-us-east-1 regions, which was accepted but not applied. S3 requires this constraint outside us-east-1.- name: Create S3 bucket for tests (host LocalStack) run: | - awslocal s3 mb s3://$AWS_STACK_NAME || true + if [ "${AWS_REGION:-us-east-1}" = "us-east-1" ]; then + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" || true + else + awslocal s3api create-bucket --bucket "$AWS_STACK_NAME" \ + --create-bucket-configuration LocationConstraint="${AWS_REGION:-us-east-1}" || true + fi awslocal s3 ls
162-163: Stop overriding GITHUB_TOKEN with GIT_PRIVATE_TOKEN.The workflow overrides
GITHUB_TOKENwithGIT_PRIVATE_TOKENacross 13 test steps. This violates least-privilege by exposing a broader-scope PAT and breaks the intended fallback logic (try private token first, then fall back to runner token).Use the default runner-provided token and keep
GIT_PRIVATE_TOKENas a separate variable only if private repo access is truly needed:GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} + GITHUB_TOKEN: ${{ github.token }}Apply this fix to all 13 test steps: lines 162–163 (k8s), 202–203 (k8s), 242–243 (k8s), and lines 294–295, 318–319, 342–343, 366–367, 390–391, 414–415, 438–439, 462–463, 486–487, 510–511 (localstack job).
288-295: Add LocalStack-specific environment variables to all localstack job test steps.The k8s job correctly includes
AWS_S3_FORCE_PATH_STYLE: 'true'andAWS_EC2_METADATA_DISABLED: 'true'for LocalStack compatibility, but the localstack job omits these across all 10 test steps. This creates inconsistent behavior and may cause unexpected LocalStack interactions.Add these variables to every test step in the localstack job. For example, lines 288–295:
AWS_ENDPOINT: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566 + AWS_S3_FORCE_PATH_STYLE: 'true' + AWS_EC2_METADATA_DISABLED: 'true' GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}Apply the same additions to all other test steps: cloud-runner-end2end-caching, cloud-runner-end2end-retaining, cloud-runner-caching, cloud-runner-environment, cloud-runner-image, cloud-runner-hooks, cloud-runner-local-persistence, cloud-runner-locking-core, and cloud-runner-locking-get-locked.
40-43: Pin k3d version to avoid supply-chain risk.The code installs k3d from the main branch without version pinning, which creates a supply-chain risk. Download a specific k3d release directly with version pinning instead.
- name: Install k3d run: | - curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + set -euo pipefail + K3D_VERSION=v5.7.3 + curl -fsSL -o /usr/local/bin/k3d "https://github.com/k3d-io/k3d/releases/download/${K3D_VERSION}/k3d-linux-amd64" + chmod +x /usr/local/bin/k3d k3d version | cat
🧹 Nitpick comments (1)
.github/workflows/cloud-runner-integrity.yml (1)
512-553: Update commented AWS job to use aws-actions/configure-aws-credentials@v4.The commented AWS job references
aws-actions/configure-aws-credentials@v1(line 529), which is deprecated and will fail on current runners. If this job is intended for future use, update to v4:- name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }}If this job will remain unused, consider removing the block entirely to reduce maintenance overhead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/cloud-runner-integrity.yml(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: Tests
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: WebGL on 2023.2.2f1
- GitHub Check: WebGL on 6000.0.36f1
- GitHub Check: StandaloneLinux64 on 2023.2.2f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: iOS on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
src/model/cloud-runner/remote-client/index.ts (4)
288-289: Align LFS folder assertion with its message.The assertion currently checks that
.git/lfsexists while the message says “LFS folder should not exist before caching”, which is contradictory:assert(fs.existsSync(path.join(`.git`, `lfs`)), 'LFS folder should not exist before caching');Either invert the condition or update the message; based on the surrounding flow (clone without LFS and only then pull from cache), the intent seems to be to assert non-existence:
- assert(fs.existsSync(path.join(`.git`, `lfs`)), 'LFS folder should not exist before caching'); + assert(!fs.existsSync(path.join(`.git`, `lfs`)), 'LFS folder should not exist before caching');
248-285: Fix unused catch bindings and improve checkout fallback clarity.The new SHA/branch/PR fetch and checkout flow is good, but the catch bindings (
_error,_error2,_error3, later_error) are either unused or only rethrown, which is what ESLint had already flagged in earlier reviews. You can simplify the try/catch structure and remove unused bindings while preserving semantics:- // Ensure refs exist (tags and PR refs) - await CloudRunnerSystem.Run(`git fetch --all --tags || true`); + // Ensure refs exist (tags and PR refs) + await CloudRunnerSystem.Run(`git fetch --all --tags || true`); ... - if (targetSha) { - try { - await CloudRunnerSystem.Run(`git checkout ${targetSha}`); - } catch (_error) { - try { - await CloudRunnerSystem.Run(`git fetch origin ${targetSha} || true`); - await CloudRunnerSystem.Run(`git checkout ${targetSha}`); - } catch (_error2) { - RemoteClientLogger.logWarning(`Falling back to branch checkout; SHA not found: ${targetSha}`); - try { - await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); - } catch (_error3) { - if ((targetBranch || '').startsWith('pull/')) { - await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); - } else { - throw _error2; - } - } - } - } - } else { - try { - await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); - } catch (_error) { - if ((targetBranch || '').startsWith('pull/')) { - await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); - } else { - throw _error; - } - } - RemoteClientLogger.log(`buildParameter Git Sha is empty`); - } + if (targetSha) { + try { + await CloudRunnerSystem.Run(`git checkout ${targetSha}`); + } catch { + try { + await CloudRunnerSystem.Run(`git fetch origin ${targetSha} || true`); + await CloudRunnerSystem.Run(`git checkout ${targetSha}`); + } catch (error) { + RemoteClientLogger.logWarning(`Falling back to branch checkout; SHA not found: ${targetSha}`); + try { + await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); + } catch { + if ((targetBranch || '').startsWith('pull/')) { + await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); + } else { + throw error; + } + } + } + } + } else { + try { + await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); + } catch (error) { + if ((targetBranch || '').startsWith('pull/')) { + await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); + } else { + throw error; + } + } + RemoteClientLogger.log(`buildParameter Git Sha is empty`); + }Also, to satisfy
lines-around-comment, consider adding a blank line before the// Ensure refs exist...comment (after the preceding log call).
309-371: LFS pull strategies never fall back due tosuppressErrorand no-op catch.In
pullLatestLFS, the multi-strategy LFS pull is conceptually great (plain pull →GIT_PRIVATE_TOKEN→GITHUB_TOKEN→ warning), but as written it won’t actually fall back:
- All
git lfs pullcalls useCloudRunnerSystem.Run(..., true), which setssuppressError = true, so non‑zero exit codes do not throw.- The first
tryblock always logs success andreturns, even ifgit lfs pullfailed.- The
catch (_error) { void 0; }is effectively unreachable for LFS failures and still trips ESLint (unused binding +void 0no‑op).This means token-based strategies and the final warning will never be exercised on LFS failures.
You can fix both correctness and style with something like:
await CloudRunnerSystem.Run(`git config --global filter.lfs.process "git-lfs filter-process"`); if (CloudRunner.buildParameters.skipLfs) { RemoteClientLogger.log(`Skipping LFS pull (skipLfs=true)`); return; } - // Best effort: try plain pull first (works for public repos or pre-configured auth) - try { - await CloudRunnerSystem.Run(`git lfs pull`, true); - await CloudRunnerSystem.Run(`git lfs checkout || true`, true); - RemoteClientLogger.log(`Pulled LFS files without explicit token configuration`); - - return; - } catch (_error) { - /* no-op: best-effort git lfs pull without tokens may fail */ - void 0; - } + // Best effort: try plain pull first (works for public repos or pre-configured auth) + try { + await CloudRunnerSystem.Run(`git lfs pull`); + await CloudRunnerSystem.Run(`git lfs checkout || true`, true); + RemoteClientLogger.log(`Pulled LFS files without explicit token configuration`); + + return; + } catch { + // no-op here: fall through to token-based strategies + } ... - // Try with GIT_PRIVATE_TOKEN - try { + // Try with GIT_PRIVATE_TOKEN + try { const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN; if (gitPrivateToken) { RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`); ... - await CloudRunnerSystem.Run(`git lfs pull`, true); - await CloudRunnerSystem.Run(`git lfs checkout || true`, true); + await CloudRunnerSystem.Run(`git lfs pull`); + await CloudRunnerSystem.Run(`git lfs checkout || true`, true); ... - // Try with GITHUB_TOKEN - try { + // Try with GITHUB_TOKEN + try { const githubToken = process.env.GITHUB_TOKEN; if (githubToken) { ... - await CloudRunnerSystem.Run(`git lfs pull`, true); - await CloudRunnerSystem.Run(`git lfs checkout || true`, true); + await CloudRunnerSystem.Run(`git lfs pull`); + await CloudRunnerSystem.Run(`git lfs checkout || true`, true);This way, failures in each strategy actually trigger the next path and, if all fail, you hit the final warning log and continue without crashing.
377-408: Retained workspace SHA/branch fallback is good; clean up catch bindings.The retained-workspace flow now:
- Logs the cache key (helpful).
- Fetches tags and PR refs.
- Does
reset --hardandcheckoutby SHA, with branch andorigin/branchfallbacks.Functionally this is solid, but the catch bindings repeat the earlier ESLint issues:
try { await CloudRunnerSystem.Run(`git reset --hard "${sha}"`); await CloudRunnerSystem.Run(`git checkout ${sha}`); } catch (_error) { RemoteClientLogger.logWarning(...); try { await CloudRunnerSystem.Run(`git checkout ${branch}`); } catch (_error2) { if ((branch || '').startsWith('pull/')) { await CloudRunnerSystem.Run(`git checkout origin/${branch}`); } else { throw _error2; } } }You can remove unused bindings and keep the rethrow behavior:
- try { - await CloudRunnerSystem.Run(`git reset --hard "${sha}"`); - await CloudRunnerSystem.Run(`git checkout ${sha}`); - } catch (_error) { + try { + await CloudRunnerSystem.Run(`git reset --hard "${sha}"`); + await CloudRunnerSystem.Run(`git checkout ${sha}`); + } catch { RemoteClientLogger.logWarning(`Retained workspace: SHA not found, falling back to branch ${branch}`); - try { - await CloudRunnerSystem.Run(`git checkout ${branch}`); - } catch (_error2) { - if ((branch || '').startsWith('pull/')) { - await CloudRunnerSystem.Run(`git checkout origin/${branch}`); - } else { - throw _error2; - } - } + try { + await CloudRunnerSystem.Run(`git checkout ${branch}`); + } catch (error) { + if ((branch || '').startsWith('pull/')) { + await CloudRunnerSystem.Run(`git checkout origin/${branch}`); + } else { + throw error; + } + } }
🧹 Nitpick comments (2)
src/model/cloud-runner/remote-client/index.ts (2)
44-52: Ensure log file writes include newlines in k8s path.In the k8s branch,
fs.appendFileSync(logFile, element);(and the lingering line write) omit a newline, so the log file can end up with concatenated lines, unlike the stdout/logging behavior.Consider appending
\nto keep the file line-oriented and consistent:- if (CloudRunnerOptions.providerStrategy === 'k8s') { - fs.appendFileSync(logFile, element); - // Write to stdout so kubectl logs can capture it - process.stdout.write(`${element}\n`); + if (CloudRunnerOptions.providerStrategy === 'k8s') { + fs.appendFileSync(logFile, `${element}\n`); + // Write to stdout so kubectl logs can capture it + process.stdout.write(`${element}\n`); ... - if (CloudRunnerOptions.providerStrategy === 'k8s') { - if (lingeringLine) { - fs.appendFileSync(logFile, lingeringLine); - process.stdout.write(`${lingeringLine}\n`); - } + if (CloudRunnerOptions.providerStrategy === 'k8s') { + if (lingeringLine) { + fs.appendFileSync(logFile, `${lingeringLine}\n`); + process.stdout.write(`${lingeringLine}\n`); + }Also applies to: 57-66
72-127: Post-build cache guards look good; minor robustness tweaks optional.The new guards for Library/Build cache pushes and the retained-workspace cleanup check are solid: they avoid throwing on missing/empty folders and log why cache writes are skipped. Logging the cache key is also helpful for debugging.
If you want to harden this further (optional), you could:
- Include
libraryFolderHost/buildFolderHostin the skip messages to aid remote debugging.- Normalize the cache destination paths via
CloudRunnerFolders.libraryCacheFolderFullrather than manual/Libraryand/buildsuffixes, to centralize path logic.No blocking issues from this block; behaviorally this is an improvement.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (1)
src/model/cloud-runner/remote-client/index.ts(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/model/cloud-runner/remote-client/index.ts (8)
src/model/cloud-runner/remote-client/remote-client-logger.ts (1)
RemoteClientLogger(7-93)src/model/cloud-runner/options/cloud-runner-folders.ts (1)
CloudRunnerFolders(6-90)src/model/cloud-runner/remote-client/caching.ts (1)
Caching(15-290)src/model/cloud-runner/services/core/cloud-runner-system.ts (1)
CloudRunnerSystem(4-69)src/model/input.ts (2)
gitPrivateToken(202-204)branch(56-64)src/model/github.ts (1)
sha(28-30)src/model/cloud-runner/options/cloud-runner-options.ts (1)
branch(88-98)src/model/versioning.ts (1)
branch(19-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: Android on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: WSAPlayer on 2021.3.32f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
🔇 Additional comments (1)
src/model/cloud-runner/remote-client/index.ts (1)
134-141: Activation success signaling is clear and k8s-friendly.Writing
"Activation successful"to bothstdoutandCloudRunnerLoggeraligns with the k8s log-streaming behavior and test assertions.No changes requested here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (5)
src/model/cloud-runner/workflows/build-automation-workflow.ts (5)
81-83: Use$LOG_FILEconsistently instead of hardcoded/home/job-log.txtYou set
LOG_FILEat line 82, but several places still hardcode/home/job-log.txt(log start in setup,game ci startlogs, and bothremote-cli-log-streaminvocations). This splits log writes between two paths and makes changing the log location harder. It would be cleaner and less error‑prone to route all log writes and log‑stream invocations through$LOG_FILE(and ensure its directory exists in the containerized branch as you already do in the non‑container branch).Also applies to: 117-121, 171-171, 197-199, 207-208
90-132: Non‑containerized setup assumes a prebuilt localdist/index.jswithout validationIn
setupCommands, the clone/fallback logic only runs whenisContainerizedis true; the non‑container branch just sets up logging. Combined withbuilderPath = process.cwd()/dist/index.jsfor non‑container runs, this assumes the builder has already been built and is present in the current working directory. If that invariant isn’t strictly guaranteed (e.g., forlocal/local-systemor future non‑container providers),node ${builderPath}calls will fail late and opaquely. Either reuse the clone logic (or a variant) for non‑container providers, or at least validate that${builderPath}exists and fail fast with a clear message / documented requirement.
162-170: Stubnode/npm/n/yarnbinaries can hide real dependency issuesCreating no‑op stubs for
node,npm,n, andyarnwhen they’re missing will cause any step that truly depends on these tools (includingnode ${builderPath} ...) to silently do nothing yet still “succeed”. If thelocal-dockerimage is misconfigured and lacks these tools, that’s likely something you want to surface, not mask. Prefer failing fast with a clear error, or at minimum log a loud warning and gate stub creation behind an explicit opt‑in flag intended only for specific test scenarios.
179-180: Post‑build error handling currently masksremote-cli-post-buildfailuresBoth containerized branches treat
remote-cli-post-buildas best‑effort: in the local‑docker path via|| echo "Post-build command completed with warnings"and in the standard containerized path via... || true. Ifremote-cli-post-buildis responsible for artifact upload, cache persistence, or other critical steps, these constructs will make the overall job succeed despite incomplete outputs. Consider capturing the exit code and either:
- Failing the job when post‑build is critical, or
- Logging a detailed warning with the exit code (and ideally stderr) while explicitly documenting that post‑build is optional in these modes.
This keeps behavior intentional and debuggable instead of silently swallowing real failures.
Also applies to: 198-199
205-209: Non‑containerizedBuildCommandsno longer stages or runs the build entrypointIn the non‑container branch you now only:
- Echo
"game ci start",- Run
remote-cli-log-streamfor 3 seconds, and- Invoke
remote-cli-post-build.There’s no longer any staging of the default build script or
/entrypoint.sh, so local/non‑container providers won’t execute actual build steps via this workflow and may appear to “succeed” while doing nothing. If the intention is still to drive the Unity build from here, you likely need to reintroduce the entrypoint/steps setup and execution similar to the containerized path, or otherwise clearly document that the non‑container provider is expected to handle the build entirely outside this workflow.
🧹 Nitpick comments (1)
src/model/cloud-runner/workflows/build-automation-workflow.ts (1)
53-61: MakeisContainerizedderivation future‑proof for new provider strategiesHard‑coding the containerized modes to
'aws' | 'k8s' | 'local-docker'means any new or plugin‑backed provider that still runs inside a container will silently fall into the non‑container path (differentbuilderPath, no apt/tooling, different logging). Consider centralizing this decision (e.g., via a provider capability flag or helper likeCloudRunner.isContainerizedStrategy(providerStrategy)) so new strategies don’t have to remember to update this switch.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/index.jsis excluded by!**/dist/**dist/index.js.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (1)
src/model/cloud-runner/workflows/build-automation-workflow.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-30T18:01:50.339Z
Learnt from: MichaelBuhler
Repo: game-ci/unity-builder PR: 685
File: .github/workflows/build-tests-ubuntu.yml:146-146
Timestamp: 2025-01-30T18:01:50.339Z
Learning: The build steps duplication in the workflow is intentional to implement a retry mechanism with different delay intervals between attempts.
Applied to files:
src/model/cloud-runner/workflows/build-automation-workflow.ts
🧬 Code graph analysis (1)
src/model/cloud-runner/workflows/build-automation-workflow.ts (2)
src/model/index.ts (1)
CloudRunner(27-27)src/model/cloud-runner/options/cloud-runner-folders.ts (1)
CloudRunnerFolders(6-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: WSAPlayer on 2022.3.13f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: Android on 2021.3.32f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: StandaloneWindows64 on 2022.3.13f1
- GitHub Check: WSAPlayer on 2021.3.32f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2023.2.2f1
- GitHub Check: iOS on 2023.2.2f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: iOS on 2021.3.45f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/model/cloud-runner/remote-client/index.ts (3)
265-305: Unused catch bindings and contradictory LFS assertion still present.The issues flagged in previous reviews remain unaddressed:
- Line 275:
_erroris unused — use parameterlesscatch- Line 279:
_error2should be renamed toerrorper ESLint conventions- Line 283:
_error3is unused — use parameterlesscatch- Line 305: The assertion message says "should not exist" but the code asserts the folder does exist
- } catch (_error) { + } catch { try { await CloudRunnerSystem.Run(`git fetch origin ${targetSha} || true`); await CloudRunnerSystem.Run(`git checkout ${targetSha}`); - } catch (_error2) { + } catch (error) { RemoteClientLogger.logWarning(`Falling back to branch checkout; SHA not found: ${targetSha}`); try { await CloudRunnerSystem.Run(`git checkout ${targetBranch}`); - } catch (_error3) { + } catch { if ((targetBranch || '').startsWith('pull/')) { await CloudRunnerSystem.Run(`git checkout origin/${targetBranch}`); } else { - throw _error2; + throw error; } } } }And fix the assertion:
- assert(fs.existsSync(path.join(`.git`, `lfs`)), 'LFS folder should not exist before caching'); + assert(!fs.existsSync(path.join(`.git`, `lfs`)), 'LFS folder should not exist before caching');
339-342: Unused catch binding and unconventional no-op still present.The previous review flagged this: the catch binding
_errorat line 339 is unused, andvoid 0;is an unconventional way to express a no-op.- } catch (_error) { - /* no-op: best-effort git lfs pull without tokens may fail */ - void 0; - } + } catch { + // no-op: best-effort git lfs pull without tokens may fail + }
411-425: Unused catch bindings in retained workspace logic still present.The previous review flagged these issues:
- Line 414:
_erroris unused — use parameterlesscatch- Line 418:
_error2should be renamed toerrorper ESLint conventionstry { await CloudRunnerSystem.Run(`git reset --hard "${sha}"`); await CloudRunnerSystem.Run(`git checkout ${sha}`); - } catch (_error) { + } catch { RemoteClientLogger.logWarning(`Retained workspace: SHA not found, falling back to branch ${branch}`); try { await CloudRunnerSystem.Run(`git checkout ${branch}`); - } catch (_error2) { + } catch (error) { if ((branch || '').startsWith('pull/')) { await CloudRunnerSystem.Run(`git checkout origin/${branch}`); } else { - throw _error2; + throw error; } } }
🧹 Nitpick comments (2)
src/model/cloud-runner/remote-client/index.ts (2)
34-38: Misleading comment:setDefaultEncodingdoesn't control buffering.The comment states this ensures stdout is "unbuffered," but
setDefaultEncodingonly sets the character encoding for write operations. Node.js stdout buffering is controlled by the stream's internal state and cannot be disabled this way. The encoding setting itself is harmless but the comment is incorrect.Consider updating the comment to reflect what the code actually does:
- // For K8s, ensure stdout is unbuffered so messages are captured immediately - if (CloudRunnerOptions.providerStrategy === 'k8s') { - process.stdout.setDefaultEncoding('utf8'); - } + // For K8s, ensure consistent UTF-8 encoding for stdout + if (CloudRunnerOptions.providerStrategy === 'k8s') { + process.stdout.setDefaultEncoding('utf8'); + }
152-155: Ineffective flush attempt.Writing an empty string to stdout does not trigger a flush in Node.js. The
isTTYcheck and empty write are essentially no-ops. If immediate flushing is critical, consider using a synchronous write approach or removing the dead code.// Write to stdout first so kubectl logs can capture it process.stdout.write(`${successMessage}\n`); - // Force flush stdout to ensure it's captured - if (process.stdout.isTTY === false) { - process.stdout.write(''); // Trigger flush - } // Also log via CloudRunnerLogger for GitHub Actions CloudRunnerLogger.log(successMessage);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/model/cloud-runner/remote-client/index.ts(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/model/cloud-runner/remote-client/index.ts (3)
src/model/cloud-runner/remote-client/remote-client-logger.ts (1)
RemoteClientLogger(7-93)src/model/cloud-runner/options/cloud-runner-folders.ts (1)
CloudRunnerFolders(6-90)src/model/cloud-runner/services/core/cloud-runner-system.ts (1)
CloudRunnerSystem(4-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: WebGL on 2022.3.13f1
- GitHub Check: StandaloneLinux64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2022.3.13f1
- GitHub Check: StandaloneOSX on 2021.3.32f1
- GitHub Check: StandaloneOSX on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: StandaloneOSX on 2021.3.45f1
- GitHub Check: WSAPlayer on 2021.3.32f1
- GitHub Check: tvOS on 2022.3.13f1
- GitHub Check: tvOS on 2021.3.32f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: tvOS on 2023.2.2f1
- GitHub Check: Android on 2022.3.13f1
- GitHub Check: WSAPlayer on 2023.2.2f1
- GitHub Check: Android on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 6000.0.36f1
- GitHub Check: StandaloneWindows64 on 2023.2.2f1
- GitHub Check: StandaloneWindows64 on 2021.3.32f1
- GitHub Check: Android on 2021.3.32f1
🔇 Additional comments (3)
src/model/cloud-runner/remote-client/index.ts (3)
88-107: LGTM: Guarded cache push with proper existence and content checks.The defensive checks for folder existence and non-empty content before pushing to cache are well implemented. The error handling appropriately warns without failing the build.
109-128: LGTM: Consistent defensive pattern for build cache.Same well-structured defensive checks as the Library cache push.
131-138: LGTM: Defensive cleanup with existence check.Checking both path formats before attempting removal prevents unnecessary errors.
| // For K8s, write to both log file and stdout so kubectl logs can capture it | ||
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | ||
| fs.appendFileSync(logFile, element); | ||
| // Write to stdout so kubectl logs can capture it - ensure newline is included | ||
| process.stdout.write(`${element}\n`); | ||
| // Force flush if possible | ||
| if (typeof process.stdout.flush === 'function') { | ||
| process.stdout.flush(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline when appending to log file.
Line 51 appends element to the log file without a newline, but line 53 writes to stdout with a newline. This will cause log file entries to be concatenated together without line breaks.
Also, process.stdout.flush does not exist in Node.js (it's a Python concept). The check on line 55 will always be false, making lines 55-57 dead code.
if (CloudRunnerOptions.providerStrategy === 'k8s') {
- fs.appendFileSync(logFile, element);
+ fs.appendFileSync(logFile, `${element}\n`);
// Write to stdout so kubectl logs can capture it - ensure newline is included
process.stdout.write(`${element}\n`);
- // Force flush if possible
- if (typeof process.stdout.flush === 'function') {
- process.stdout.flush();
- }
CloudRunnerLogger.log(element);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // For K8s, write to both log file and stdout so kubectl logs can capture it | |
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | |
| fs.appendFileSync(logFile, element); | |
| // Write to stdout so kubectl logs can capture it - ensure newline is included | |
| process.stdout.write(`${element}\n`); | |
| // Force flush if possible | |
| if (typeof process.stdout.flush === 'function') { | |
| process.stdout.flush(); | |
| } | |
| // For K8s, write to both log file and stdout so kubectl logs can capture it | |
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | |
| fs.appendFileSync(logFile, `${element}\n`); | |
| // Write to stdout so kubectl logs can capture it - ensure newline is included | |
| process.stdout.write(`${element}\n`); | |
| CloudRunnerLogger.log(element); |
🤖 Prompt for AI Agents
In src/model/cloud-runner/remote-client/index.ts around lines 49-57, the code
appends element to the log file without a newline and contains a dead check for
process.stdout.flush; change the file write to append a newline (e.g. append
element + '\n') so file entries are line-separated, and remove the
process.stdout.flush block (or replace it with nothing) since
process.stdout.flush is not a Node.js API.
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | ||
| if (lingeringLine) { | ||
| fs.appendFileSync(logFile, lingeringLine); | ||
| process.stdout.write(`${lingeringLine}\n`); | ||
| if (typeof process.stdout.flush === 'function') { | ||
| process.stdout.flush(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same newline and flush issues as in the data handler.
Line 68 has the same missing newline issue, and lines 70-72 contain the same dead code for the non-existent flush method.
if (CloudRunnerOptions.providerStrategy === 'k8s') {
if (lingeringLine) {
- fs.appendFileSync(logFile, lingeringLine);
+ fs.appendFileSync(logFile, `${lingeringLine}\n`);
process.stdout.write(`${lingeringLine}\n`);
- if (typeof process.stdout.flush === 'function') {
- process.stdout.flush();
- }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | |
| if (lingeringLine) { | |
| fs.appendFileSync(logFile, lingeringLine); | |
| process.stdout.write(`${lingeringLine}\n`); | |
| if (typeof process.stdout.flush === 'function') { | |
| process.stdout.flush(); | |
| } | |
| if (CloudRunnerOptions.providerStrategy === 'k8s') { | |
| if (lingeringLine) { | |
| fs.appendFileSync(logFile, `${lingeringLine}\n`); | |
| process.stdout.write(`${lingeringLine}\n`); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/model/cloud-runner/remote-client/index.ts around lines 66 to 72, the
handler writes lingeringLine without a trailing newline and contains dead code
checking for a non-existent process.stdout.flush method; append a newline when
writing to the log and stdout (e.g., use lingeringLine + '\n' for both
fs.appendFileSync and process.stdout.write) and remove the conditional block
that checks/calls process.stdout.flush so there is no dead-code path.
Changes
Related Issues
Related PRs
Successful Workflow Run Link
PRs don't have access to secrets so you will need to provide a link to a successful run of the workflows from your own
repo.
Checklist
code of conduct
in the documentation repo)
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Tests
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.